This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 874c7d7056 Fix a few deprecation warnings.
874c7d7056 is described below

commit 874c7d7056ede39844ad63be875b95d130c3d0b5
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Sun Jul 16 23:37:54 2023 +0200

    Fix a few deprecation warnings.
---
 .../apache/sis/console/FormattedOutputCommand.java |   2 +-
 .../java/org/apache/sis/feature/Validator.java     |   1 +
 .../java/org/apache/sis/image/ImageProcessor.java  |   3 +
 .../org/apache/sis/feature/CustomAttribute.java    |   3 +-
 .../org/apache/sis/feature/FeatureTestCase.java    |  14 ++-
 .../apache/sis/feature/SingletonAttributeTest.java |   1 +
 .../apache/sis/internal/jaxb/gco/GO_Record.java    |   2 +-
 .../internal/metadata/TransformationAccuracy.java  |   1 +
 .../metadata/iso/distribution/DefaultDataFile.java |   1 +
 .../sis/metadata/iso/quality/AbstractElement.java  |   1 +
 .../iso/quality/AbstractTemporalQuality.java       |   1 +
 .../metadata/iso/quality/DefaultDataQuality.java   |   1 +
 ...DefaultNonQuantitativeAttributeCorrectness.java |   1 +
 .../iso/quality/DefaultQuantitativeResult.java     |   1 +
 .../org/apache/sis/metadata/sql/Dispatcher.java    |   2 +-
 .../org/apache/sis/util/iso/DefaultRecordType.java |   5 +-
 .../sis/metadata/iso/DefaultMetadataTest.java      |   2 +-
 .../apache/sis/metadata/iso/MarshallingTest.java   |  14 +--
 .../java/org/apache/sis/test/sql/TestDatabase.java |   2 -
 .../sis/util/iso/DefaultRecordSchemaTest.java      |   3 +-
 .../org/apache/sis/util/iso/DefaultRecordTest.java |   8 +-
 .../apache/sis/util/iso/DefaultRecordTypeTest.java |   1 +
 .../referencing/PositionalAccuracyConstant.java    |   3 +-
 .../provider/MolodenskyInterpolation.java          |   2 +
 .../apache/sis/io/wkt/GeodeticObjectParser.java    |   1 +
 .../transform/DefaultMathTransformFactory.java     |   2 +
 .../transform/InterpolatedMolodenskyTransform.java |   2 +
 .../InterpolatedMolodenskyTransform2D.java         |   2 +
 .../referencing/provider/ProvidersTest.java        |   1 +
 .../sis/referencing/ImmutableIdentifierTest.java   |   1 +
 .../sis/test/integration/DatumShiftTest.java       |   2 +-
 .../sis/test/integration/MetadataVerticalTest.java |   2 +-
 .../sis/internal/converter/StringConverter.java    |   4 +-
 .../org/apache/sis/internal/system/Reflect.java    |   2 +-
 .../apache/sis/internal/util/PropertyFormat.java   |   2 +-
 .../sis/util/DefaultInternationalString.java       |   5 +-
 .../src/main/java/org/apache/sis/util/Locales.java |   2 +
 .../sis/internal/converter/PathConverterTest.java  |  11 +-
 .../internal/converter/StringConverterTest.java    |   2 +-
 .../src/test/java/org/apache/sis/test/Assume.java  |   4 +-
 .../test/java/org/apache/sis/util/LocalesTest.java |   2 +
 .../apache/sis/storage/landsat/MetadataReader.java |   5 +-
 .../apache/sis/storage/geotiff/GeoTiffStore.java   |   3 +-
 .../org/apache/sis/internal/netcdf/TestCase.java   |   7 +-
 .../sis/internal/sql/feature/ValueGetter.java      |   4 +-
 .../sis/internal/storage/MetadataBuilder.java      |  38 ++-----
 .../apache/sis/internal/storage/PRJDataStore.java  |  13 ++-
 .../org/apache/sis/internal/storage/csv/Store.java |   2 +-
 .../sis/internal/storage/esri/RasterStore.java     |   3 +-
 .../apache/sis/internal/storage/folder/Store.java  |   3 +-
 .../sis/internal/storage/io/IOUtilities.java       | 119 ++++++++++++---------
 .../sis/internal/storage/io/IOUtilitiesTest.java   |  61 ++++++-----
 .../sis/internal/storage/gpx/WriterTest.java       |   2 +
 53 files changed, 217 insertions(+), 165 deletions(-)

diff --git 
a/application/sis-console/src/main/java/org/apache/sis/console/FormattedOutputCommand.java
 
b/application/sis-console/src/main/java/org/apache/sis/console/FormattedOutputCommand.java
index 7d01a2891e..17527daa4f 100644
--- 
a/application/sis-console/src/main/java/org/apache/sis/console/FormattedOutputCommand.java
+++ 
b/application/sis-console/src/main/java/org/apache/sis/console/FormattedOutputCommand.java
@@ -212,7 +212,7 @@ abstract class FormattedOutputCommand extends CommandRunner 
{
      * @throws JAXBException if an error occurred while producing the XML 
output using JAXB.
      * @throws IOException should never happen since we are appending to a 
print writer.
      */
-    @SuppressWarnings("UseOfSystemOutOrSystemErr")
+    @SuppressWarnings({"UseOfSystemOutOrSystemErr", "deprecation"})
     final void format(final Object object) throws DataStoreException, 
JAXBException, IOException {
         switch (outputFormat) {
             case TEXT: {
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java 
b/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java
index 4ea92464f9..6c8ead25a9 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java
@@ -86,6 +86,7 @@ final class Validator {
      * @param  explanation  explanation of the constraint violation.
      * @return the {@code report}, or a new report if {@code report} was null.
      */
+    @SuppressWarnings("deprecation")
     private AbstractElement addViolationReport(AbstractElement report,
             final PropertyType type, final InternationalString explanation)
     {
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java 
b/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
index 9b92aecd31..333156b27c 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
@@ -1281,6 +1281,7 @@ public class ImageProcessor implements Cloneable {
      * @param  colors  colors to use for each range of values in the source 
image.
      * @deprecated Replaced by {@link #visualize(RenderedImage)} with colors 
map inferred from the {@link Colorizer}.
      */
+    @SuppressWarnings("removal")
     @Deprecated(since="1.4", forRemoval=true)
     public synchronized RenderedImage visualize(final RenderedImage source, 
final Map<NumberRange<?>,Color[]> colors) {
         /*
@@ -1305,6 +1306,7 @@ public class ImageProcessor implements Cloneable {
      * @param  ranges  description of {@code source} bands, or {@code null} if 
none. This is typically
      *                 obtained by {@link 
org.apache.sis.coverage.grid.GridCoverage#getSampleDimensions()}.
      */
+    @SuppressWarnings("removal")
     @Deprecated(since="1.4", forRemoval=true)
     public RenderedImage visualize(final RenderedImage source, final 
List<SampleDimension> ranges) {
         ArgumentChecks.ensureNonNull("source", source);
@@ -1458,6 +1460,7 @@ public class ImageProcessor implements Cloneable {
      * @param  ranges  description of {@code source} bands, or {@code null} if 
none. This is typically
      *                 obtained by {@link 
org.apache.sis.coverage.grid.GridCoverage#getSampleDimensions()}.
      */
+    @SuppressWarnings("removal")
     @Deprecated(since="1.4", forRemoval=true)
     public RenderedImage visualize(final RenderedImage source, final Rectangle 
bounds, final MathTransform toSource,
                                    final List<SampleDimension> ranges)
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/feature/CustomAttribute.java 
b/core/sis-feature/src/test/java/org/apache/sis/feature/CustomAttribute.java
index 48fc8926f3..8de8967a57 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/feature/CustomAttribute.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/feature/CustomAttribute.java
@@ -33,7 +33,7 @@ import org.opengis.feature.AttributeType;
  * This implementation adds its own criterion to the attribute quality 
evaluation.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.5
+ * @version 1.4
  * @since   0.5
  */
 @SuppressWarnings("serial")
@@ -76,6 +76,7 @@ final class CustomAttribute<V> extends AbstractAttribute<V> {
      * Evaluates the quality of this attribute with a custom rule.
      */
     @Override
+    @SuppressWarnings("deprecation")
     public DataQuality quality() {
         final DefaultDataQuality        quality = (DefaultDataQuality) 
super.quality();
         final DefaultDomainConsistency  report  = new 
DefaultDomainConsistency();
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java 
b/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java
index 36d2ab7904..bb62f5db81 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java
@@ -19,6 +19,7 @@ package org.apache.sis.feature;
 import java.util.Map;
 import java.util.List;
 import java.util.Collection;
+import org.opengis.util.InternationalString;
 import org.opengis.metadata.quality.DataQuality;
 import org.opengis.metadata.quality.Element;
 import org.opengis.metadata.quality.Result;
@@ -45,7 +46,7 @@ import org.opengis.feature.Property;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Marc le Bihan
- * @version 0.8
+ * @version 1.4
  * @since   0.5
  */
 public abstract class FeatureTestCase extends TestCase {
@@ -330,14 +331,18 @@ public abstract class FeatureTestCase extends TestCase {
             int numOccurrences = 0;
             final DataQuality quality = verifyQualityReports("population");
             for (final Element report : quality.getReports()) {
+                @SuppressWarnings("deprecation")
                 final String identifier = 
report.getMeasureIdentification().toString();
                 if (identifier.equals("city")) {
                     numOccurrences++;
                     final Result result = 
TestUtilities.getSingleton(report.getResults());
                     assertInstanceOf("result", QuantitativeResult.class, 
result);
+
+                    @SuppressWarnings("deprecation")
+                    final InternationalString error = ((QuantitativeResult) 
result).getErrorStatistic();
                     assertEquals("quality.report.result.errorStatistic",
                             CustomAttribute.ADDITIONAL_QUALITY_INFO,
-                            String.valueOf(((QuantitativeResult) 
result).getErrorStatistic()));
+                            String.valueOf(error));
                 }
             }
             assertEquals("Number of reports.", 1, numOccurrences);
@@ -381,8 +386,9 @@ public abstract class FeatureTestCase extends TestCase {
                 if (result instanceof ConformanceResult && 
!((ConformanceResult) result).pass()) {
                     assertTrue("Too many reports", anomalyIndex < 
anomalousProperties.length);
                     final String propertyName = 
anomalousProperties[anomalyIndex];
-                    final String identifier   = 
report.getMeasureIdentification().toString();
-                    final String explanation  = ((ConformanceResult) 
result).getExplanation().toString();
+                    @SuppressWarnings("deprecation")
+                    final String identifier   = 
String.valueOf(report.getMeasureIdentification());
+                    final String explanation  = 
String.valueOf(((ConformanceResult) result).getExplanation());
                     assertEquals("quality.report.measureIdentification", 
propertyName, identifier);
                     assertTrue  ("quality.report.result.explanation", 
explanation.contains(propertyName));
                     anomalyIndex++;
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java
 
b/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java
index b6ce21cb71..fde89024e4 100644
--- 
a/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java
+++ 
b/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java
@@ -116,6 +116,7 @@ public final class SingletonAttributeTest extends TestCase {
      * @param  explanation   the expected explanation.
      * @param  consistency   the report element to test.
      */
+    @SuppressWarnings("deprecation")
     private static void assertDomainConsistencyEquals(final String 
propertyName, final String explanation,
             final DomainConsistency consistency)
     {
diff --git 
a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Record.java
 
b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Record.java
index b0ab926d59..d6fb401e65 100644
--- 
a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Record.java
+++ 
b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Record.java
@@ -85,7 +85,7 @@ public class GO_Record extends PropertyType<GO_Record, 
Record> {
      * @param  metadata  the unmarshalled metadata.
      */
     public final void setElement(final DefaultRecord metadata) {
-        if (!metadata.getAttributes().isEmpty()) {
+        if (!metadata.getFields().isEmpty()) {
             this.metadata = metadata;
         }
     }
diff --git 
a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/TransformationAccuracy.java
 
b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/TransformationAccuracy.java
index 94edf600cd..27a0867e21 100644
--- 
a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/TransformationAccuracy.java
+++ 
b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/TransformationAccuracy.java
@@ -62,6 +62,7 @@ public final class TransformationAccuracy extends Static {
      * @param  accuracy  the accuracy in metres.
      * @return a positional accuracy with the given value.
      */
+    @SuppressWarnings("deprecation")
     public static PositionalAccuracy create(final Double accuracy) {
         PositionalAccuracy p = CACHE.get(accuracy);
         if (p == null) {
diff --git 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/DefaultDataFile.java
 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/DefaultDataFile.java
index 45b008708c..eef5b1659f 100644
--- 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/DefaultDataFile.java
+++ 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/DefaultDataFile.java
@@ -126,6 +126,7 @@ public class DefaultDataFile extends ISOMetadata implements 
DataFile {
      *
      * @see #castOrCopy(DataFile)
      */
+    @SuppressWarnings("deprecation")
     public DefaultDataFile(final DataFile object) {
         super(object);
         if (object != null) {
diff --git 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/AbstractElement.java
 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/AbstractElement.java
index b8a28f8d04..b7805439b3 100644
--- 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/AbstractElement.java
+++ 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/AbstractElement.java
@@ -101,6 +101,7 @@ import static 
org.apache.sis.util.collection.Containers.isNullOrEmpty;
     AbstractMetaquality.class,
     DefaultQualityMeasure.class     // Not a subclass, but "weakly" associated.
 })
+@SuppressWarnings("deprecation")
 public class AbstractElement extends ISOMetadata implements Element {
     /**
      * Serial number for inter-operability with different versions.
diff --git 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/AbstractTemporalQuality.java
 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/AbstractTemporalQuality.java
index 9e501feb85..c025eb7e66 100644
--- 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/AbstractTemporalQuality.java
+++ 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/AbstractTemporalQuality.java
@@ -55,6 +55,7 @@ import org.opengis.metadata.quality.TemporalAccuracy;
     DefaultTemporalConsistency.class,
     DefaultTemporalValidity.class
 })
+@SuppressWarnings("deprecation")
 public class AbstractTemporalQuality extends AbstractElement implements 
TemporalQuality {
     /**
      * Serial number for inter-operability with different versions.
diff --git 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultDataQuality.java
 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultDataQuality.java
index 23828525d4..8729c012d9 100644
--- 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultDataQuality.java
+++ 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultDataQuality.java
@@ -136,6 +136,7 @@ public class DefaultDataQuality extends ISOMetadata 
implements DataQuality {
      *
      * @see #castOrCopy(DataQuality)
      */
+    @SuppressWarnings("deprecation")
     public DefaultDataQuality(final DataQuality object) {
         super(object);
         if (object != null) {
diff --git 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultNonQuantitativeAttributeCorrectness.java
 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultNonQuantitativeAttributeCorrectness.java
index e98269e9fd..195d67988b 100644
--- 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultNonQuantitativeAttributeCorrectness.java
+++ 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultNonQuantitativeAttributeCorrectness.java
@@ -50,6 +50,7 @@ import 
org.opengis.metadata.quality.NonQuantitativeAttributeCorrectness;
 @XmlSeeAlso({
     DefaultNonQuantitativeAttributeAccuracy.class
 })
+@SuppressWarnings("deprecation")
 public class DefaultNonQuantitativeAttributeCorrectness extends 
AbstractThematicAccuracy
         implements NonQuantitativeAttributeCorrectness
 {
diff --git 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultQuantitativeResult.java
 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultQuantitativeResult.java
index f71700d502..096d380d2c 100644
--- 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultQuantitativeResult.java
+++ 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultQuantitativeResult.java
@@ -109,6 +109,7 @@ public class DefaultQuantitativeResult extends 
AbstractResult implements Quantit
      *
      * @see #castOrCopy(QuantitativeResult)
      */
+    @SuppressWarnings("deprecation")
     public DefaultQuantitativeResult(final QuantitativeResult object) {
         super(object);
         if (object != null) {
diff --git 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
index 274e8a67a6..9d48372338 100644
--- 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
+++ 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
@@ -231,7 +231,7 @@ final class Dispatcher implements InvocationHandler {
                             if (impl == null) {
                                 return value;
                             }
-                            cache = impl.newInstance();
+                            cache = 
impl.getDeclaredConstructor().newInstance();
                             if (cache instanceof ModifiableMetadata) {
                                 ((ModifiableMetadata) 
cache).transitionTo(ModifiableMetadata.State.COMPLETABLE);
                             }
diff --git 
a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultRecordType.java
 
b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultRecordType.java
index 08232653cb..8ac75ba397 100644
--- 
a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultRecordType.java
+++ 
b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultRecordType.java
@@ -115,7 +115,7 @@ public class DefaultRecordType extends RecordDefinition 
implements RecordType, S
      *
      * @see #getContainer()
      */
-    @SuppressWarnings("serial")
+    @SuppressWarnings({"serial", "deprecation"})
     private final RecordSchema container;
 
     /**
@@ -130,6 +130,7 @@ public class DefaultRecordType extends RecordDefinition 
implements RecordType, S
      *
      * @param other  the {@code RecordType} to copy.
      */
+    @SuppressWarnings("deprecation")
     public DefaultRecordType(final RecordType other) {
         typeName   = other.getTypeName();
         container  = other.getContainer();
@@ -401,7 +402,7 @@ public class DefaultRecordType extends RecordDefinition 
implements RecordType, S
      */
     @Override
     public boolean isInstance(final Record record) {
-        return (record != null) && 
getMembers().containsAll(record.getAttributes().keySet());
+        return (record != null) && 
getMembers().containsAll(record.getFields().keySet());
     }
 
     /**
diff --git 
a/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/DefaultMetadataTest.java
 
b/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/DefaultMetadataTest.java
index 14bb447e07..1b39fa94dd 100644
--- 
a/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/DefaultMetadataTest.java
+++ 
b/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/DefaultMetadataTest.java
@@ -138,7 +138,7 @@ public final class DefaultMetadataTest extends TestCase {
      */
     @SuppressWarnings("deprecation")
     private static void assertLanguagesEquals(final DefaultMetadata metadata, 
final Locale... expected) {
-        assertArrayEquals("languages", expected,    
metadata.getLanguages().toArray());
+        assertArrayEquals("languages", expected,    
metadata.getLocalesAndCharsets().keySet().toArray());
         assertEquals     ("language",  expected[0], metadata.getLanguage());
         assertArrayEquals("locales",   Arrays.copyOfRange(expected, 1, 
expected.length), metadata.getLocales().toArray());
     }
diff --git 
a/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/MarshallingTest.java
 
b/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/MarshallingTest.java
index 3641ae4686..6ecc79c1e8 100644
--- 
a/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/MarshallingTest.java
+++ 
b/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/MarshallingTest.java
@@ -143,13 +143,10 @@ public final class MarshallingTest extends TestUsingFile 
implements Filter {
             id.setCodeSpace("md.id.ns");
             md.setMetadataIdentifier(id);
         }
-        // Languages — one language only, and one (country, language) tuple.
-        final Collection<Locale> languages = List.of(Locale.ENGLISH, 
Locale.CANADA_FRENCH);
-        md.setLanguages(languages);
-
-        // Character Sets (character encoding)
-        final Collection<Charset> charSets = 
Set.of(StandardCharsets.ISO_8859_1);
-        md.setCharacterSets(charSets);
+        final Map<Locale,Charset> languages = Map.of(
+                Locale.ENGLISH,       StandardCharsets.ISO_8859_1,
+                Locale.CANADA_FRENCH, StandardCharsets.ISO_8859_1);
+        md.setLocalesAndCharsets(languages);
         {
             // Parent metadata
             final DefaultCitation parent = new DefaultCitation("A parent 
metadata");
@@ -516,8 +513,7 @@ public final class MarshallingTest extends TestUsingFile 
implements Filter {
             associatedResources = Set.of(associatedResource);
             dataId.setAssociatedResources(associatedResources);
         }
-        dataId.setLanguages(languages);     // Locales (ISO 19115:2014) a.k.a 
Languages and CharacterSets (ISO 19115:2003)
-        dataId.setCharacterSets(charSets);
+        dataId.setLocalesAndCharsets(languages);     // Locales (ISO 
19115:2014) a.k.a Languages and CharacterSets (ISO 19115:2003)
         dataId.setEnvironmentDescription (new SimpleInternationalString("High 
humidity."));
         dataId.setSupplementalInformation(new SimpleInternationalString("High 
water pressure."));
         {
diff --git 
a/core/sis-metadata/src/test/java/org/apache/sis/test/sql/TestDatabase.java 
b/core/sis-metadata/src/test/java/org/apache/sis/test/sql/TestDatabase.java
index d5bdcad545..5a0dc63121 100644
--- a/core/sis-metadata/src/test/java/org/apache/sis/test/sql/TestDatabase.java
+++ b/core/sis-metadata/src/test/java/org/apache/sis/test/sql/TestDatabase.java
@@ -26,7 +26,6 @@ import java.sql.Statement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.SQLDataException;
-import org.postgresql.PGProperty;
 import org.postgresql.ds.PGSimpleDataSource;
 import org.apache.derby.jdbc.EmbeddedDataSource;
 import org.apache.sis.internal.metadata.sql.Dialect;
@@ -237,7 +236,6 @@ public class TestDatabase implements AutoCloseable {
         ds.setDatabaseName(NAME);
         ds.setApplicationName("Apache SIS test database");
         ds.setCurrentSchema(schema);
-        ds.setProperty(PGProperty.LOGGER_LEVEL, "OFF");   // For avoiding 
warning when no PostgreSQL server is running.
         /*
          * Current version does not use pooling on the assumption that 
connections to local host are fast enough.
          * We verify that the schema does not exist, even if the `create` 
argument is `false`, because we assume
diff --git 
a/core/sis-metadata/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java
 
b/core/sis-metadata/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java
index fb65d9acfc..ee2e3b8105 100644
--- 
a/core/sis-metadata/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java
+++ 
b/core/sis-metadata/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java
@@ -47,6 +47,7 @@ public final class DefaultRecordSchemaTest extends TestCase {
      * Tests {@link DefaultRecordSchema#createRecordType(CharSequence, Map)}.
      */
     @Test
+    @SuppressWarnings("deprecation")
     public void testCreateRecordType() {
         final DefaultRecordSchema schema = new DefaultRecordSchema(null, null, 
"MySchema");
         final Map<CharSequence,Class<?>> fields = new LinkedHashMap<>(8);
@@ -104,7 +105,7 @@ public final class DefaultRecordSchemaTest extends TestCase 
{
          * The DefaultRecordType(TypeName, RecordSchema, Map) constructor 
performs many argument checks, so
          * we use that constructor as a way to perform a final validation, 
especially regarding namespaces.
          */
-        final DefaultRecordType copy = new DefaultRecordType(
+        final var copy = new DefaultRecordType(
                 recordType.getTypeName(),
                 recordType.getContainer(),
                 recordType.getFieldTypes());
diff --git 
a/core/sis-metadata/src/test/java/org/apache/sis/util/iso/DefaultRecordTest.java
 
b/core/sis-metadata/src/test/java/org/apache/sis/util/iso/DefaultRecordTest.java
index 50cba65534..e1f29e3a58 100644
--- 
a/core/sis-metadata/src/test/java/org/apache/sis/util/iso/DefaultRecordTest.java
+++ 
b/core/sis-metadata/src/test/java/org/apache/sis/util/iso/DefaultRecordTest.java
@@ -38,7 +38,7 @@ import static 
org.apache.sis.test.Assertions.assertSerializedEquals;
  * Tests the {@link DefaultRecord} implementation.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.5
+ * @version 1.4
  * @since   0.5
  */
 @DependsOn(DefaultRecordSchemaTest.class)
@@ -78,7 +78,7 @@ public final class DefaultRecordTest extends TestCase {
      */
     private static void setAllAndCompare(final DefaultRecord record, final 
Object... values) {
         record.setAll(values);
-        assertArrayEquals("attributes.values", values, 
record.getAttributes().values().toArray());
+        assertArrayEquals("attributes.values", values, 
record.getFields().values().toArray());
     }
 
     /**
@@ -112,7 +112,7 @@ public final class DefaultRecordTest extends TestCase {
         final DefaultRecord record = new DefaultRecord(recordType);
         assertSame("recordType", recordType, record.getRecordType());
         int index = 0;
-        for (final Map.Entry<MemberName,Object> entry : 
record.getAttributes().entrySet()) {
+        for (final Map.Entry<MemberName,Object> entry : 
record.getFields().entrySet()) {
             final String name;
             final Object value;
             switch (index) {
@@ -191,7 +191,7 @@ public final class DefaultRecordTest extends TestCase {
          */
         assertEquals("baseValueClass", Double.TYPE, 
record.definition.baseValueClass());
         assertArrayEquals("attributes.values", new Double[] {0.0, 0.0},
-                record.getAttributes().values().toArray());
+                record.getFields().values().toArray());
         /*
          * Combines tests similar to 3 other test methods in this class.
          */
diff --git 
a/core/sis-metadata/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java
 
b/core/sis-metadata/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java
index 0ddfdd2fb2..c7baec1f1b 100644
--- 
a/core/sis-metadata/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java
+++ 
b/core/sis-metadata/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java
@@ -77,6 +77,7 @@ public final class DefaultRecordTypeTest extends TestCase {
      * {@link DefaultRecordType#locate(MemberName)}.
      */
     @Test
+    @SuppressWarnings("deprecation")
     public void testConstructor() {
         init();
         final DefaultRecordType type = create();
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/PositionalAccuracyConstant.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/PositionalAccuracyConstant.java
index 48230d4912..8dd2e30efd 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/PositionalAccuracyConstant.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/PositionalAccuracyConstant.java
@@ -123,6 +123,7 @@ public final class PositionalAccuracyConstant extends 
DefaultAbsoluteExternalPos
     /**
      * Creates an positional accuracy initialized to the given result.
      */
+    @SuppressWarnings("deprecation")
     private PositionalAccuracyConstant(final InternationalString 
measureDescription,
             final InternationalString evaluationMethodDescription, final 
boolean pass)
     {
@@ -185,7 +186,7 @@ public final class PositionalAccuracyConstant extends 
DefaultAbsoluteExternalPos
                         if (Units.isLinear(unit)) {
                             final Unit<Length> unitOfLength = 
unit.asType(Length.class);
                             for (final Record record : records) {
-                                for (final Object value : 
record.getAttributes().values()) {
+                                for (final Object value : 
record.getFields().values()) {
                                     if (value instanceof Number) {
                                         double v = ((Number) 
value).doubleValue();
                                         v = 
unitOfLength.getConverterTo(Units.METRE).convert(v);
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MolodenskyInterpolation.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MolodenskyInterpolation.java
index abc116296d..c9b4c4e3e9 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MolodenskyInterpolation.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MolodenskyInterpolation.java
@@ -86,6 +86,7 @@ public final class MolodenskyInterpolation extends 
FranceGeocentricInterpolation
     /**
      * Constructs a provider for the given number of dimensions.
      */
+    @SuppressWarnings("removal")
     private MolodenskyInterpolation(ParameterDescriptorGroup parameters, int 
indexOfDim) {
         super(parameters, indexOfDim);
     }
@@ -95,6 +96,7 @@ public final class MolodenskyInterpolation extends 
FranceGeocentricInterpolation
      * after all parameters have been processed.
      */
     @Override
+    @SuppressWarnings("removal")
     MathTransform createGeodeticTransformation(final MathTransformFactory 
factory,
             final Ellipsoid source, final Ellipsoid target, final boolean 
withHeights,
             final DatumShiftGridFile<Angle,Length> grid) throws 
FactoryException
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
index a628e877d2..865f2c4394 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
@@ -1261,6 +1261,7 @@ class GeodeticObjectParser extends MathTransformParser 
implements Comparator<Coo
         int dimension = 2;
         String csType = WKTKeywords.ellipsoidal;
         if (method != null) {
+            @SuppressWarnings("deprecation")
             final Integer d = method.getSourceDimensions();
             if (d != null) dimension = d;
             if (method instanceof AbstractProvider) {
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
index 783eca7656..2e92bc472a 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
@@ -1060,7 +1060,9 @@ public class DefaultMathTransformFactory extends 
AbstractFactory implements Math
              * one.
              */
             if (method instanceof AbstractProvider) {
+                @SuppressWarnings("deprecation")
                 final Integer sourceDim = (sourceCS != null) ? 
sourceCS.getDimension() : method.getSourceDimensions();
+                @SuppressWarnings("deprecation")
                 final Integer targetDim = (targetCS != null) ? 
targetCS.getDimension() : method.getTargetDimensions();
                 if (sourceDim != null && targetDim != null) {
                     method = ((AbstractProvider) 
method).redimension(sourceDim, targetDim);
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedMolodenskyTransform.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedMolodenskyTransform.java
index 5ece127daf..9f71464802 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedMolodenskyTransform.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedMolodenskyTransform.java
@@ -148,6 +148,7 @@ public class InterpolatedMolodenskyTransform extends 
MolodenskyFormula {
      *
      * @see #createGeodeticTransformation(MathTransformFactory, Ellipsoid, 
boolean, Ellipsoid, boolean, DatumShiftGrid)
      */
+    @SuppressWarnings("removal")
     protected InterpolatedMolodenskyTransform(final Ellipsoid source, final 
boolean isSource3D,
                                               final Ellipsoid target, final 
boolean isTarget3D,
                                               final 
DatumShiftGrid<Angle,Length> grid)
@@ -193,6 +194,7 @@ public class InterpolatedMolodenskyTransform extends 
MolodenskyFormula {
      * @return the transformation between geographic coordinates in degrees.
      * @throws FactoryException if an error occurred while creating a 
transform.
      */
+    @SuppressWarnings("removal")
     public static MathTransform createGeodeticTransformation(final 
MathTransformFactory factory,
             final Ellipsoid source, final boolean isSource3D,
             final Ellipsoid target, final boolean isTarget3D,
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedMolodenskyTransform2D.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedMolodenskyTransform2D.java
index 5cc19d8ce7..d97b9a1a2d 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedMolodenskyTransform2D.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedMolodenskyTransform2D.java
@@ -36,6 +36,7 @@ import org.apache.sis.referencing.datum.DatumShiftGrid;
  *
  * @see <a href="https://issues.apache.org/jira/browse/SIS-500";>Deprecate (for 
removal) InterpolatedMolodenskyTransform</a>
  */
+@SuppressWarnings("removal")
 @Deprecated(since="1.4", forRemoval=true)
 final class InterpolatedMolodenskyTransform2D extends 
InterpolatedMolodenskyTransform implements MathTransform2D {
     /**
@@ -89,6 +90,7 @@ final class InterpolatedMolodenskyTransform2D extends 
InterpolatedMolodenskyTran
      * @version 0.7
      * @since   0.7
      */
+    @SuppressWarnings("removal")
     static final class Inverse extends InterpolatedMolodenskyTransform.Inverse 
implements MathTransform2D {
         /**
          * Serial number for inter-operability with different versions.
diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
 
b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
index 14cac2b1b5..d7d479634a 100644
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
+++ 
b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
@@ -49,6 +49,7 @@ public final class ProvidersTest extends TestCase {
     /**
      * Returns all providers to test.
      */
+    @SuppressWarnings("removal")
     private static Class<?>[] methods() {
         return new Class<?>[] {
             Affine.class,
diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/ImmutableIdentifierTest.java
 
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/ImmutableIdentifierTest.java
index 36a0cb353d..618a61a505 100644
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/ImmutableIdentifierTest.java
+++ 
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/ImmutableIdentifierTest.java
@@ -79,6 +79,7 @@ public final class ImmutableIdentifierTest extends TestCase {
         assertEquals     ("description_fr",    "Voici une description",  
identifier.getDescription().toString(Locale.FRENCH));
         assertEquals     ("description_fr_CA", "Pareil",                 
identifier.getDescription().toString(Locale.CANADA_FRENCH));
         assertEquals     ("description_fr_BE", "Voici une description",  
identifier.getDescription().toString(new Locale("fr", "BE")));
+        // TODO: use Locale.of(…) with JDK19.
     }
 
     /**
diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/test/integration/DatumShiftTest.java
 
b/core/sis-referencing/src/test/java/org/apache/sis/test/integration/DatumShiftTest.java
index 3ee02e4f98..f5cd745518 100644
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/test/integration/DatumShiftTest.java
+++ 
b/core/sis-referencing/src/test/java/org/apache/sis/test/integration/DatumShiftTest.java
@@ -25,7 +25,7 @@ import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
-import static org.apache.sis.test.Assume.*;
+import static org.apache.sis.test.Assume.assumeDataExists;
 
 
 /**
diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/test/integration/MetadataVerticalTest.java
 
b/core/sis-referencing/src/test/java/org/apache/sis/test/integration/MetadataVerticalTest.java
index 9da38465c8..ba0f247cb7 100644
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/test/integration/MetadataVerticalTest.java
+++ 
b/core/sis-referencing/src/test/java/org/apache/sis/test/integration/MetadataVerticalTest.java
@@ -157,7 +157,7 @@ public class MetadataVerticalTest extends TestCase {
         assertInstanceOf("citation", NilObject.class, citation);
         assertEquals("nilReason", NilReason.MISSING, ((NilObject) 
citation).getNilReason());
         assertEquals("abstract", "SIS test", 
identification.getAbstract().toString());
-        assertEquals("language", Locale.ENGLISH, 
getSingleton(identification.getLanguages()));
+        assertEquals("language", Locale.ENGLISH, 
getSingleton(identification.getLocalesAndCharsets().keySet()));
         /*
          * <gmd:geographicElement>
          *   <gmd:EX_GeographicBoundingBox>
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/converter/StringConverter.java
 
b/core/sis-utility/src/main/java/org/apache/sis/internal/converter/StringConverter.java
index 142629c27c..367e5ddfa9 100644
--- 
a/core/sis-utility/src/main/java/org/apache/sis/internal/converter/StringConverter.java
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/internal/converter/StringConverter.java
@@ -296,8 +296,8 @@ abstract class StringConverter<T> extends 
SystemConverter<String, T> {
         private static final long serialVersionUID = -6518011235037500143L;
         public URL() {super(java.net.URL.class);}                              
     // Instantiated by ServiceLoader.
 
-        @Override java.net.URL doConvert(String source) throws 
MalformedURLException {
-            return new java.net.URL(source);
+        @Override java.net.URL doConvert(String source) throws 
URISyntaxException, MalformedURLException {
+            return new java.net.URI(source).parseServerAuthority().toURL();
         }
     }
 
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/system/Reflect.java 
b/core/sis-utility/src/main/java/org/apache/sis/internal/system/Reflect.java
index 35808d7986..5854380b88 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/system/Reflect.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/system/Reflect.java
@@ -23,7 +23,7 @@ import java.util.function.Consumer;
 
 
 /**
- * Contextual information fetching by reflection.
+ * Contextual information fetched by reflection.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Guilhem Legal (Geomatys)
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/util/PropertyFormat.java
 
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/PropertyFormat.java
index c8899e315b..6b4b9d472c 100644
--- 
a/core/sis-utility/src/main/java/org/apache/sis/internal/util/PropertyFormat.java
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/PropertyFormat.java
@@ -121,7 +121,7 @@ public abstract class PropertyFormat extends LineAppender 
implements Localized {
             final Locale locale = getLocale();
             text = (locale != Locale.ROOT) ? ((Currency) 
value).getDisplayName(locale) : value.toString();
         } else if (value instanceof Record) {
-            appendCollection(((Record) value).getAttributes().values(), 
recursive);
+            appendCollection(((Record) value).getFields().values(), recursive);
             return;
         } else if (value instanceof Iterable<?>) {
             appendCollection((Iterable<?>) value, recursive);
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/util/DefaultInternationalString.java
 
b/core/sis-utility/src/main/java/org/apache/sis/util/DefaultInternationalString.java
index 728d437d2b..36e1755d88 100644
--- 
a/core/sis-utility/src/main/java/org/apache/sis/util/DefaultInternationalString.java
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/util/DefaultInternationalString.java
@@ -44,7 +44,7 @@ import org.apache.sis.util.collection.Containers;
  * SIS typically references them as if they were immutable because of their 
<cite>add-only</cite> behavior.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 1.1
+ * @version 1.4
  *
  * @see org.apache.sis.util.iso.Types#toInternationalString(Map, String)
  *
@@ -188,6 +188,7 @@ public class DefaultInternationalString extends 
AbstractInternationalString impl
      * @param  locale  the locale to look for, or {@code null}.
      * @return the string in the specified locale, or {@code null} if none was 
found.
      */
+    @SuppressWarnings("deprecation")
     private String getString(Locale locale) {
         while (locale != null) {
             final String text = localeMap.get(locale);
@@ -198,10 +199,12 @@ public class DefaultInternationalString extends 
AbstractInternationalString impl
             final String country  = locale.getCountry ();
             final String variant  = locale.getVariant ();
             if (!variant.isEmpty()) {
+                // TODO: replace by Locale.of(…) with JDK19.
                 locale = new Locale(language, country);
                 continue;
             }
             if (!country.isEmpty()) {
+                // TODO: replace by Locale.of(…) with JDK19.
                 locale = new Locale(language);
                 continue;
             }
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/Locales.java 
b/core/sis-utility/src/main/java/org/apache/sis/util/Locales.java
index 9496f735c0..bb01a2872b 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/Locales.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/Locales.java
@@ -210,6 +210,7 @@ filter: for (final Locale locale : locales) {
      * @param  locales  the locales from which to get the languages.
      * @return the languages, without country or variant information.
      */
+    @SuppressWarnings("deprecation")
     private static Locale[] getLanguages(final Locale... locales) {
         final Set<String> codes = new 
LinkedHashSet<>(hashMapCapacity(locales.length));
         for (final Locale locale : locales) {
@@ -218,6 +219,7 @@ filter: for (final Locale locale : locales) {
         int i=0;
         final Locale[] languages = new Locale[codes.size()];
         for (final String code : codes) {
+            // TODO: replace by Locale.of(…) with JDK19.
             languages[i++] = unique(new Locale(code));
         }
         return languages;
diff --git 
a/core/sis-utility/src/test/java/org/apache/sis/internal/converter/PathConverterTest.java
 
b/core/sis-utility/src/test/java/org/apache/sis/internal/converter/PathConverterTest.java
index 0588b795cc..15a558d819 100644
--- 
a/core/sis-utility/src/test/java/org/apache/sis/internal/converter/PathConverterTest.java
+++ 
b/core/sis-utility/src/test/java/org/apache/sis/internal/converter/PathConverterTest.java
@@ -101,7 +101,7 @@ public final class PathConverterTest extends TestCase {
     public void testFile_URL() throws MalformedURLException {
         assumeUnixRoot();
         final ObjectConverter<File,URL> c = PathConverter.FileURL.INSTANCE;
-        runInvertibleConversion(c, new File("/home/user/index.txt"), new 
URL("file:/home/user/index.txt"));
+        runInvertibleConversion(c, new File("/home/user/index.txt"), 
URI.create("file:/home/user/index.txt").toURL());
         assertSerializedEquals(c);
     }
 
@@ -126,7 +126,7 @@ public final class PathConverterTest extends TestCase {
     @Test
     public void testURI_URL() throws MalformedURLException, URISyntaxException 
{
         final ObjectConverter<URI,URL> c = PathConverter.URI_URL.INSTANCE;
-        runInvertibleConversion(c, new URI("file:/home/user/index.txt"), new 
URL("file:/home/user/index.txt"));
+        runInvertibleConversion(c, new URI("file:/home/user/index.txt"), 
URI.create("file:/home/user/index.txt").toURL());
         assertSerializedEquals(c);
     }
 
@@ -152,7 +152,7 @@ public final class PathConverterTest extends TestCase {
     @Test
     public void testURL_String() throws MalformedURLException {
         final ObjectConverter<URL,String> c = new 
StringConverter.URL().inverse();
-        runInvertibleConversion(c, new URL("file:/home/user/index.txt"), 
"file:/home/user/index.txt");
+        runInvertibleConversion(c, 
URI.create("file:/home/user/index.txt").toURL(), "file:/home/user/index.txt");
         assertSerializedEquals(c);
     }
 
@@ -164,8 +164,9 @@ public final class PathConverterTest extends TestCase {
      */
     @Test
     public void testURL_URI() throws MalformedURLException, URISyntaxException 
{
+        final URI uri = new URI("file:/home/user/index.txt");
         final ObjectConverter<URL,URI> c = PathConverter.URL_URI.INSTANCE;
-        runInvertibleConversion(c, new URL("file:/home/user/index.txt"), new 
URI("file:/home/user/index.txt"));
+        runInvertibleConversion(c, uri.toURL(), uri);
         assertSerializedEquals(c);
     }
 
@@ -179,7 +180,7 @@ public final class PathConverterTest extends TestCase {
     public void testURL_File() throws MalformedURLException {
         assumeUnixRoot();
         final ObjectConverter<URL,File> c = PathConverter.URLFile.INSTANCE;
-        runInvertibleConversion(c, new URL("file:/home/user/index.txt"), new 
File("/home/user/index.txt"));
+        runInvertibleConversion(c, 
URI.create("file:/home/user/index.txt").toURL(), new 
File("/home/user/index.txt"));
         assertSerializedEquals(c);
     }
 }
diff --git 
a/core/sis-utility/src/test/java/org/apache/sis/internal/converter/StringConverterTest.java
 
b/core/sis-utility/src/test/java/org/apache/sis/internal/converter/StringConverterTest.java
index 50f285df82..9897d347c3 100644
--- 
a/core/sis-utility/src/test/java/org/apache/sis/internal/converter/StringConverterTest.java
+++ 
b/core/sis-utility/src/test/java/org/apache/sis/internal/converter/StringConverterTest.java
@@ -288,7 +288,7 @@ public final class StringConverterTest extends TestCase {
     @Test
     public void testURL() throws MalformedURLException {
         final ObjectConverter<String,URL> c = new StringConverter.URL();
-        runInvertibleConversion(c, "file:/home/user/index.txt", new 
URL("file:/home/user/index.txt"));
+        runInvertibleConversion(c, "file:/home/user/index.txt", 
URI.create("file:/home/user/index.txt").toURL());
         assertSerializedEquals(c);
     }
 
diff --git a/core/sis-utility/src/test/java/org/apache/sis/test/Assume.java 
b/core/sis-utility/src/test/java/org/apache/sis/test/Assume.java
index efe64d3fcd..365b4c7c8f 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/test/Assume.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/test/Assume.java
@@ -21,6 +21,8 @@ import java.nio.file.Path;
 import java.nio.file.Files;
 import org.apache.sis.internal.system.DataDirectory;
 
+import static org.junit.Assume.*;
+
 
 /**
  * Assumption methods used by the SIS project in addition of the JUnit ones.
@@ -29,7 +31,7 @@ import org.apache.sis.internal.system.DataDirectory;
  * @version 1.4
  * @since   0.7
  */
-public final class Assume extends org.junit.Assume {
+public final class Assume {
     /**
      * Do not allow instantiation.
      */
diff --git 
a/core/sis-utility/src/test/java/org/apache/sis/util/LocalesTest.java 
b/core/sis-utility/src/test/java/org/apache/sis/util/LocalesTest.java
index 72d16f7547..ef848828f3 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/util/LocalesTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/util/LocalesTest.java
@@ -63,6 +63,7 @@ public final class LocalesTest extends TestCase {
      */
     @Test
     public void testUnique() {
+        // TODO: use Locale.of(…) with JDK19.
         assertSame(Locale.ENGLISH, Locales.unique(new Locale("en")));
         assertSame(Locale.FRENCH,  Locales.unique(new Locale("fr")));
     }
@@ -83,6 +84,7 @@ public final class LocalesTest extends TestCase {
         assertSame(Locale.JAPAN,         Locales.parse("ja_JP"));
         assertSame(Locale.US,            Locales.parse("en; USA"));
 
+        // TODO: use Locale.of(…) with JDK19.
         assertEquals(new Locale("de", "DE"),            
Locales.parse("de_DE"));
         assertEquals(new Locale("",   "GB"),            Locales.parse("_GB"));
         assertEquals(new Locale("en", "US", "WINDOWS"), 
Locales.parse("en_US_WINDOWS"));
diff --git 
a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/MetadataReader.java
 
b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/MetadataReader.java
index 4f84643f68..901473663e 100644
--- 
a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/MetadataReader.java
+++ 
b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/MetadataReader.java
@@ -19,6 +19,7 @@ package org.apache.sis.storage.landsat;
 import java.awt.Dimension;
 import java.io.BufferedReader;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.Optional;
 import java.util.Arrays;
 import java.util.Date;
@@ -96,7 +97,7 @@ import static 
org.apache.sis.internal.util.CollectionsExt.singletonOrNull;
  * @author  Thi Phuong Hao Nguyen (VNSC)
  * @author  Rémi Maréchal (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.4
  * @since   0.8
  */
 final class MetadataReader extends MetadataBuilder {
@@ -856,7 +857,7 @@ final class MetadataReader extends MetadataBuilder {
      * @throws FactoryException if an error occurred while creating the 
Coordinate Reference System.
      */
     final Metadata getMetadata() throws FactoryException {
-        addLanguage(Locale.ENGLISH, MetadataBuilder.Scope.METADATA);
+        addLanguage(Locale.ENGLISH, StandardCharsets.US_ASCII, 
MetadataBuilder.Scope.METADATA);
         addResourceScope(ScopeCode.COVERAGE, null);
         addTopicCategory(TopicCategory.GEOSCIENTIFIC_INFORMATION);
         try {
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java
index 4858494bfa..d4bde598dc 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java
@@ -17,6 +17,7 @@
 package org.apache.sis.storage.geotiff;
 
 import java.util.List;
+import java.util.Locale;
 import java.util.Optional;
 import java.util.logging.LogRecord;
 import java.net.URI;
@@ -293,7 +294,7 @@ public class GeoTiffStore extends DataStore implements 
Aggregate {
             builder.addFormatName(Constants.GEOTIFF);
             listeners.warning(e);
         }
-        builder.addEncoding(encoding, MetadataBuilder.Scope.METADATA);
+        builder.addLanguage(Locale.ENGLISH, encoding, 
MetadataBuilder.Scope.METADATA);
         builder.addResourceScope(ScopeCode.COVERAGE, null);
     }
 
diff --git 
a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/TestCase.java 
b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/TestCase.java
index ebe37c7989..f939c4332b 100644
--- 
a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/TestCase.java
+++ 
b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/TestCase.java
@@ -29,7 +29,9 @@ import org.apache.sis.setup.GeometryLibrary;
 import org.apache.sis.storage.event.StoreListeners;
 import org.apache.sis.storage.DataStoreMock;
 import org.opengis.test.dataset.TestData;
+import ucar.nc2.dataset.NetcdfDatasets;
 import ucar.nc2.dataset.NetcdfDataset;
+import ucar.nc2.NetcdfFiles;
 import ucar.nc2.NetcdfFile;
 import org.junit.AfterClass;
 
@@ -89,7 +91,7 @@ public abstract class TestCase extends 
org.apache.sis.test.TestCase {
          */
         String location = file.location().toString();
         location = location.substring(location.lastIndexOf('/') + 1);
-        return NetcdfFile.openInMemory(location, file.content());
+        return NetcdfFiles.openInMemory(location, file.content());
     }
 
     /**
@@ -111,7 +113,8 @@ public abstract class TestCase extends 
org.apache.sis.test.TestCase {
      * @throws DataStoreException if a logical error occurred.
      */
     protected Decoder createDecoder(final TestData file) throws IOException, 
DataStoreException {
-        return new DecoderWrapper(new NetcdfDataset(createUCAR(file)), 
GeometryLibrary.JAVA2D, createListeners());
+        final NetcdfDataset ds = NetcdfDatasets.enhance(createUCAR(file), 
NetcdfDataset.getDefaultEnhanceMode(), null);
+        return new DecoderWrapper(ds, GeometryLibrary.JAVA2D, 
createListeners());
     }
 
     /**
diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/ValueGetter.java
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/ValueGetter.java
index 1676b22bf3..803c0d1002 100644
--- 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/ValueGetter.java
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/ValueGetter.java
@@ -400,8 +400,8 @@ public class ValueGetter<T> {
      * <h4>Implementation note</h4>
      * PostgreSQL always return the time in the local time zone, while HSQLDB 
and H2 return the time as
      * inserted in the database but ignoring the timezone offset. The latter 
implies that we don't know
-     * how convert a HSQLDB and H2 to local or UTC timezone. Current 
implementation assumes PostgreSQL
-     * behavior, which is the only one that we can map to {@link 
OffsetDateTime}.
+     * how to convert a HSQLDB and H2 date to local or UTC timezone. Current 
implementation assumes the
+     * PostgreSQL behavior, which is the only one that we can map to {@link 
OffsetDateTime}.
      * Specifying a {@link java.util.Calendar} seems to have no effect.
      */
     static final class AsOffsetDateTime extends ValueGetter<OffsetDateTime> {
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
index 2f335d3d03..4c8348815f 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
@@ -1036,44 +1036,20 @@ public class MetadataBuilder {
      * Storage locations are:
      *
      * <ul>
-     *   <li><b>Metadata:</b> {@code metadata/language}</li>
-     *   <li><b>Resource:</b> {@code metadata/identificationInfo/language}</li>
+     *   <li><b>Metadata:</b> {@code metadata/defaultLocale} or {@code 
metadata/otherLocale}</li>
+     *   <li><b>Resource:</b> {@code 
metadata/identificationInfo/defaultLocale} or
+     *                        {@code 
metadata/identificationInfo/otherLocale}</li>
      * </ul>
      *
      * @param  language  a language used for documenting data and/or metadata, 
or {@code null} for no-operation.
+     * @param  encoding  the encoding associated to the locale, or {@code 
null} if unspecified.
      * @param  scope     whether the language applies to data, to metadata or 
to both.
-     *
-     * @see #addEncoding(Charset, MetadataBuilder.Scope)
      */
-    public final void addLanguage(final Locale language, final Scope scope) {
+    public final void addLanguage(final Locale language, final Charset 
encoding, final Scope scope) {
         ArgumentChecks.ensureNonNull("scope", scope);
         if (language != null) {
-            // No need to use `addIfNotPresent(…)` because Locale collection 
is a Set by default.
-            if (scope != Scope.RESOURCE) 
metadata().getLanguages().add(language);
-            if (scope != Scope.METADATA) 
identification().getLanguages().add(language);
-        }
-    }
-
-    /**
-     * Adds a character set used for encoding the data and/or metadata.
-     * Storage locations are:
-     *
-     * <ul>
-     *   <li><b>Metadata:</b> {@code metadata/characterSet}</li>
-     *   <li><b>Resource:</b> {@code 
metadata/identificationInfo/characterSet}</li>
-     * </ul>
-     *
-     * @param  encoding  the character set used for encoding data and/or 
metadata, or {@code null} for no-operation.
-     * @param  scope     whether the encoding applies to data, to metadata or 
to both.
-     *
-     * @see #addLanguage(Locale, MetadataBuilder.Scope)
-     */
-    public final void addEncoding(final Charset encoding, final Scope scope) {
-        ArgumentChecks.ensureNonNull("scope", scope);
-        if (encoding != null) {
-            // No need to use `addIfNotPresent(…)` because Charset collection 
is a Set by default.
-            if (scope != Scope.RESOURCE) 
metadata().getCharacterSets().add(encoding);
-            if (scope != Scope.METADATA) 
identification().getCharacterSets().add(encoding);
+            if (scope != Scope.RESOURCE) 
metadata().getLocalesAndCharsets().put(language, encoding);
+            if (scope != Scope.METADATA) 
identification().getLocalesAndCharsets().put(language, encoding);
         }
     }
 
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java
index c5912cc2f0..699b1b32ac 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java
@@ -17,6 +17,8 @@
 package org.apache.sis.internal.storage;
 
 import java.net.URL;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -66,7 +68,7 @@ import org.apache.sis.util.Classes;
  * be null and the CRS defined by the {@code DataOptionKey} will be used.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.3
+ * @version 1.4
  * @since   1.2
  */
 public abstract class PRJDataStore extends URIDataStore {
@@ -202,13 +204,16 @@ public abstract class PRJDataStore extends URIDataStore {
             path   = path.resolveSibling(base.concat(extension));
             stream = Files.newInputStream(path);
             source = path;
-        } else {
-            final URL url = IOUtilities.toAuxiliaryURL(location, extension);
-            if (url == null) {
+        } else try {
+            final URI uri = IOUtilities.toAuxiliaryURI(location, extension);
+            if (uri == null) {
                 return null;
             }
+            final URL url = uri.toURL();
             stream = url.openStream();
             source = url;
+        } catch (URISyntaxException e) {
+            throw new 
DataStoreException(Resources.format(Resources.Keys.CanNotReadAuxiliaryFile_1, 
"*." + extension), e);
         }
         /*
          * Reads the auxiliary file fully, with an arbitrary size limit.
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
index 3bc737deea..e87174afc7 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
@@ -641,7 +641,7 @@ final class Store extends URIDataStore implements 
FeatureSet {
                 builder.addFormatName(format);
                 listeners.warning(e);
             }
-            builder.addEncoding(encoding, MetadataBuilder.Scope.ALL);
+            builder.addLanguage(Locale.ENGLISH, encoding, 
MetadataBuilder.Scope.ALL);
             builder.addResourceScope(ScopeCode.FEATURE, null);
             try {
                 builder.addExtent(envelope);
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/RasterStore.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/RasterStore.java
index 9de8ce1903..5226f189f9 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/RasterStore.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/RasterStore.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.Arrays;
 import java.util.Optional;
 import java.util.Hashtable;
+import java.util.Locale;
 import java.util.logging.Level;
 import java.io.IOException;
 import java.io.FileNotFoundException;
@@ -174,7 +175,7 @@ abstract class RasterStore extends PRJDataStore implements 
GridCoverageResource
             listeners.warning(e);
         }
         builder.addResourceScope(ScopeCode.COVERAGE, null);
-        builder.addEncoding(encoding, MetadataBuilder.Scope.METADATA);
+        builder.addLanguage(Locale.ENGLISH, encoding, 
MetadataBuilder.Scope.METADATA);
         builder.addSpatialRepresentation(null, gridGeometry, true);
         try {
             builder.addExtent(gridGeometry.getEnvelope());
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
index b701cdfe1c..08a047c56a 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
@@ -272,8 +272,7 @@ class Store extends DataStore implements StoreResource, 
UnstructuredAggregate, D
         if (metadata == null) {
             final MetadataBuilder mb = new MetadataBuilder();
             mb.addResourceScope(ScopeCode.COLLECTION, 
Resources.formatInternational(Resources.Keys.DirectoryContent_1, 
getDisplayName()));
-            mb.addLanguage(locale,   MetadataBuilder.Scope.RESOURCE);
-            mb.addEncoding(encoding, MetadataBuilder.Scope.RESOURCE);
+            mb.addLanguage(locale, encoding, MetadataBuilder.Scope.RESOURCE);
             final GenericName identifier = identifier(null);
             String name = null;
             if (identifier != null) {
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
index 456a1a7549..39e165ec11 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
@@ -204,22 +204,20 @@ public final class IOUtilities extends Static {
     }
 
     /**
-     * Converts the given {@link URI} to a {@link URL} with the same path 
except for the file extension,
-     * which is replaced by the given extension. This method is used for 
opening auxiliary files such as
-     * {@code "*.prj"} and {@code "*.tfw"} files that come with e.g. TIFF 
files.
+     * Converts the given URI to a new URI with the same path except for the 
file extension,
+     * which is replaced by the given extension. This method is used for 
opening auxiliary files
+     * such as {@code "*.prj"} and {@code "*.tfw"} files that come with e.g. 
TIFF files.
      *
      * @param  location   the URI to convert to a URL with a different 
extension, or {@code null}.
      * @param  extension  the file extension (without {@code '.'}) of the 
auxiliary file.
-     * @return URL for the auxiliary file with the given extension, or {@code 
null} if none.
-     * @throws MalformedURLException if the URI uses an unknown protocol or a 
negative port number other than -1.
-     *
-     * @since 1.2
+     * @return URI for the auxiliary file with the given extension, or {@code 
null} if none.
+     * @throws URISyntaxException if the URI cannot be reconstructed.
      */
-    public static URL toAuxiliaryURL(final URI location, final String 
extension) throws MalformedURLException {
+    public static URI toAuxiliaryURI(final URI location, final String 
extension) throws URISyntaxException {
         if (location == null || !location.isAbsolute() || location.isOpaque()) 
{
             return null;
         }
-        String path = location.getRawPath();    // Raw because URL constructor 
needs encoded strings.
+        String path = location.getRawPath();
         int s = path.indexOf('?');              // Shall be before '#' in a 
valid URL.
         if (s < 0) {
             s = path.indexOf('#');              // A '?' after '#' would be 
part of the anchor.
@@ -233,10 +231,9 @@ public final class IOUtilities extends Static {
         } else {
             path = path + '.' + extension;
         }
-        return new URL(location.getScheme(),            // http, https, file 
or jar.
+        return new URI(location.getScheme(),            // http, https, file 
or jar.
                        location.getRawAuthority(),      // Host name or 
literal IP address.
-                       location.getPort(),              // -1 if undefined.
-                       path);
+                       path, null, null);
     }
 
     /**
@@ -316,13 +313,43 @@ public final class IOUtilities extends Static {
         return (buffer != null) ? buffer.toString() : path;
     }
 
+    /**
+     * Converts a path specified as a character string to an URL.
+     * This method can be used as a replacement for the deprecated {@link URL} 
constructors.
+     *
+     * @param  url       the path to convert, or {@code null}.
+     * @param  encoding  if the URL is encoded in a {@code 
application/x-www-form-urlencoded} MIME format,
+     *                   the character encoding (normally {@code "UTF-8"}).
+     *                   If the URL is not encoded, then {@code null}.
+     * @return the path converted to an uRL, or {@code null} if the given path 
was null.
+     * @throws MalformedURLException if the path can not be parsed as an URL.
+     * @throws IOException if a non-null {@code encoding} was specified and an 
encoding error is found.
+     *
+     * @since 1.4
+     */
+    public static URL toURL(String url, final String encoding) throws 
IOException {
+        if (url == null) {
+            return null;
+        }
+        if (encoding != null) {
+            url = URLDecoder.decode(url, encoding);
+        }
+        url = encodeURI(url);
+        try {
+            return new URI(url).parseServerAuthority().toURL();
+        } catch (IllegalArgumentException | URISyntaxException cause) {
+            throw (MalformedURLException) new 
MalformedURLException(malformed(url, cause)).initCause(cause);
+        }
+    }
+
     /**
      * Converts a {@link URL} to a {@link URI}. This is equivalent to a call 
to the standard {@link URL#toURI()}
      * method, except for the following functionalities:
      *
      * <ul>
      *   <li>Optionally decodes the {@code "%XX"} sequences, where {@code 
"XX"} is a number.</li>
-     *   <li>Converts various exceptions into subclasses of {@link 
IOException}.</li>
+     *   <li>Escape spaces and a some other reserved characters.</li>
+     *   <li>Converts exceptions into subclasses of {@link IOException}.</li>
      * </ul>
      *
      * @param  url       the URL to convert, or {@code null}.
@@ -333,77 +360,72 @@ public final class IOUtilities extends Static {
      * @throws IOException if the URL cannot be converted to a URI.
      *
      * @see URI#URI(String)
+     * @see URL#toURI()
      */
     public static URI toURI(final URL url, final String encoding) throws 
IOException {
         if (url == null) {
             return null;
         }
         /*
-         * Convert the URL to a URI, taking in account the encoding if any.
-         *
-         * Note: URL.toURI() is implemented as new URI(URL.toString()) where 
toString()
-         * delegates to toExternalForm(), and all those methods are final. So 
we really
-         * don't lost anything by doing those steps ourself.
+         * Convert the URL to a URI, taking in account the encoding if any. We 
want to escape
+         * spaces with `encodeURI(…)` before to convert. Invoking 
`URL.toURI()` is preferable
+         * to `new URI(String)` because the former performs more checks, but 
is possible only
+         * if the decoding and escaping resulted in no change.
          */
-        String path = url.toExternalForm();
+        final String specified = url.toExternalForm();
+        String path = specified;
         if (encoding != null) {
             path = URLDecoder.decode(path, encoding);
         }
         path = encodeURI(path);
         try {
-            return new URI(path);
+            return path.equals(specified) ? url.toURI() : new URI(path);
         } catch (URISyntaxException cause) {
             /*
              * Occurs only if the URL is not compliant with RFC 2396. 
Otherwise every URL
              * should succeed, so a failure can actually be considered as a 
malformed URL.
              */
-            throw (MalformedURLException) new 
MalformedURLException(Exceptions.formatChainedMessages(null,
-                    Errors.format(Errors.Keys.IllegalArgumentValue_2, "URL", 
path), cause)).initCause(cause);
+            throw (MalformedURLException) new 
MalformedURLException(malformed(url, cause)).initCause(cause);
         }
     }
 
     /**
      * Converts a {@link URL} to a {@link File}. This is equivalent to a call 
to the standard
      * {@link URL#toURI()} method followed by a call to the {@link 
File#File(URI)} constructor,
-     * except for the following functionalities:
-     *
-     * <ul>
-     *   <li>Optionally decodes the {@code "%XX"} sequences, where {@code 
"XX"} is a number.</li>
-     *   <li>Converts various exceptions into subclasses of {@link 
IOException}.</li>
-     * </ul>
+     * except that exceptions are converted to {@link IOException}:
      *
-     * @param  url       the URL to convert, or {@code null}.
-     * @param  encoding  if the URL is encoded in a {@code 
application/x-www-form-urlencoded} MIME format,
-     *                   the character encoding (normally {@code "UTF-8"}). If 
the URL is not encoded,
-     *                   then {@code null}.
+     * @param  url  the URL to convert, or {@code null}.
      * @return the file for the given URL, or {@code null} if the given URL 
was null.
      * @throws IOException if the URL cannot be converted to a file.
      *
      * @see File#File(URI)
      */
-    public static File toFile(final URL url, final String encoding) throws 
IOException {
+    public static File toFile(final URL url) throws IOException {
         if (url == null) {
             return null;
-        }
-        final URI uri = toURI(url, encoding);
-        /*
-         * We really want to call the File constructor expecting a URI 
argument,
-         * not the constructor expecting a String argument, because the one for
-         * the URI argument performs additional platform-specific parsing.
-         */
-        try {
-            return new File(uri);
-        } catch (IllegalArgumentException cause) {
+        } else try {
+            return new File(url.toURI());
+        } catch (IllegalArgumentException | URISyntaxException cause) {
             /*
              * Typically happen when the URI scheme is not "file". But may 
also happen if the
              * URI contains fragment that cannot be represented in a File 
(e.g. a Query part).
              * The IllegalArgumentException does not allow us to distinguish 
those cases.
              */
-            throw new IOException(Exceptions.formatChainedMessages(null,
-                    Errors.format(Errors.Keys.IllegalArgumentValue_2, "URL", 
url), cause), cause);
+            throw new IOException(malformed(url, cause), cause);
         }
     }
 
+    /**
+     * Prepares a message for a malformed URL.
+     *
+     * @param url    the malformed URL.
+     * @param cause  the exception thrown.
+     */
+    private static String malformed(final Object url, final Exception cause) {
+        return Exceptions.formatChainedMessages(null,
+                Errors.format(Errors.Keys.IllegalArgumentValue_2, "URL", url), 
cause);
+    }
+
     /**
      * Converts a {@link URL} to a {@link Path}. This is equivalent to a call 
to the standard
      * {@link URL#toURI()} method followed by a call to the {@link 
Path#of(URI)} static method,
@@ -431,8 +453,7 @@ public final class IOUtilities extends Static {
         try {
             return Path.of(uri);
         } catch (IllegalArgumentException | FileSystemNotFoundException cause) 
{
-            final String message = Exceptions.formatChainedMessages(null,
-                    Errors.format(Errors.Keys.IllegalArgumentValue_2, "URL", 
url), cause);
+            final String message = malformed(url, cause);
             /*
              * If the exception is IllegalArgumentException, then the URI 
scheme has been recognized
              * but the URI syntax is illegal for that file system. So we can 
consider that the URL is
@@ -486,10 +507,10 @@ public final class IOUtilities extends Static {
                 return new File(path);
             }
         }
-        final URL url = new URL(path);
+        final URL url = toURL(path, encoding);
         final String scheme = url.getProtocol();
         if (scheme != null && scheme.equalsIgnoreCase("file")) {
-            return toFile(url, encoding);
+            return toFile(url);
         }
         /*
          * Leave the URL in its original encoding on the assumption that this 
is the encoding expected by
diff --git 
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/IOUtilitiesTest.java
 
b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/IOUtilitiesTest.java
index 57650ad0c8..2fe397f311 100644
--- 
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/IOUtilitiesTest.java
+++ 
b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/IOUtilitiesTest.java
@@ -47,11 +47,12 @@ public final class IOUtilitiesTest extends TestCase {
      */
     @Test
     public void testFilename() throws URISyntaxException, 
MalformedURLException {
-        assertEquals("Map.png", IOUtilities.filename(              
"/Users/name/Map.png"));
-        assertEquals("Map.png", IOUtilities.filename(new File(     
"/Users/name/Map.png")));
-        assertEquals("Map.png", IOUtilities.filename(new URI 
("file:/Users/name/Map.png")));
-        assertEquals("Map.png", IOUtilities.filename(new URL 
("file:/Users/name/Map.png")));
-        assertEquals("name",    IOUtilities.filename(new URI 
("file:/Users/name/")));
+        final URI uri = new URI ("file:/Users/name/Map.png");
+        assertEquals("Map.png", IOUtilities.filename(         
"/Users/name/Map.png"));
+        assertEquals("Map.png", IOUtilities.filename(new 
File("/Users/name/Map.png")));
+        assertEquals("Map.png", IOUtilities.filename(uri));
+        assertEquals("Map.png", IOUtilities.filename(uri.toURL()));
+        assertEquals("name",    IOUtilities.filename(new 
URI("file:/Users/name/")));
         assertEquals("",        IOUtilities.filename("/"));
         assertEquals("",        IOUtilities.filename(""));
         assertNull(IOUtilities.filename(Boolean.FALSE));
@@ -67,12 +68,13 @@ public final class IOUtilitiesTest extends TestCase {
     @Test
     @DependsOnMethod("testFilename")
     public void testExtension() throws URISyntaxException, 
MalformedURLException {
-        assertEquals("png", IOUtilities.extension(              
"/Users/name/Map.png"));
-        assertEquals("png", IOUtilities.extension(new File(     
"/Users/name/Map.png")));
-        assertEquals("png", IOUtilities.extension(new URI 
("file:/Users/name/Map.png")));
-        assertEquals("png", IOUtilities.extension(new URL 
("file:/Users/name/Map.png")));
-        assertEquals("",    IOUtilities.extension(new File(     
"/Users/name/Map")));
-        assertEquals("",    IOUtilities.extension(new File(     
"/Users/name/.png")));
+        final URI uri = new URI ("file:/Users/name/Map.png");
+        assertEquals("png", IOUtilities.extension(         
"/Users/name/Map.png"));
+        assertEquals("png", IOUtilities.extension(new 
File("/Users/name/Map.png")));
+        assertEquals("png", IOUtilities.extension(uri));
+        assertEquals("png", IOUtilities.extension(uri.toURL()));
+        assertEquals("",    IOUtilities.extension(new 
File("/Users/name/Map")));
+        assertEquals("",    IOUtilities.extension(new 
File("/Users/name/.png")));
         assertNull(IOUtilities.extension(Boolean.FALSE));
         assertNull(IOUtilities.extension(null));
     }
@@ -92,7 +94,7 @@ public final class IOUtilitiesTest extends TestCase {
         assertEquals("Any.xml", IOUtilities.filename (uri));
         assertEquals(    "xml", IOUtilities.extension(uri));
 
-        final URL url = new 
URL("jar:file:/sis-storage-tests.jar!/org/apache/sis/Any.xml");
+        final URL url = new 
URI("jar:file:/sis-storage-tests.jar!/org/apache/sis/Any.xml").toURL();
         assertEquals("Any.xml", IOUtilities.filename (url));
         assertEquals(    "xml", IOUtilities.extension(url));
     }
@@ -106,23 +108,24 @@ public final class IOUtilitiesTest extends TestCase {
     @Test
     public void testToString() throws URISyntaxException, 
MalformedURLException {
         // Do not test File because the result is platform-specific.
-        assertEquals("/Users/name/Map.png",      IOUtilities.toString(         
     "/Users/name/Map.png"));
-        assertEquals("file:/Users/name/Map.png", IOUtilities.toString(new URI 
("file:/Users/name/Map.png")));
-        assertEquals("file:/Users/name/Map.png", IOUtilities.toString(new URL 
("file:/Users/name/Map.png")));
+        final URI uri = new URI ("file:/Users/name/Map.png");
+        assertEquals("/Users/name/Map.png",      
IOUtilities.toString("/Users/name/Map.png"));
+        assertEquals("file:/Users/name/Map.png", IOUtilities.toString(uri));
+        assertEquals("file:/Users/name/Map.png", 
IOUtilities.toString(uri.toURL()));
         assertNull(IOUtilities.toString(Boolean.FALSE));
         assertNull(IOUtilities.toString(null));
     }
 
     /**
-     * Tests {@link IOUtilities#toAuxiliaryURL(URI, int)}.
+     * Tests {@link IOUtilities#toAuxiliaryURI(URI, int)}.
      *
      * @throws URISyntaxException if a URI cannot be parsed.
      * @throws MalformedURLException if a URL cannot be parsed.
      */
     @Test
-    public void testAuxiliaryURL() throws URISyntaxException, 
MalformedURLException {
-        assertEquals(new URL("http://localhost/directory/image.tfw";),
-                IOUtilities.toAuxiliaryURL(new 
URI("http://localhost/directory/image.tiff";), "tfw"));
+    public void testAuxiliaryURI() throws URISyntaxException, 
MalformedURLException {
+        assertEquals(new URI("http://localhost/directory/image.tfw";),
+                IOUtilities.toAuxiliaryURI(new 
URI("http://localhost/directory/image.tiff";), "tfw"));
     }
 
     /**
@@ -153,6 +156,7 @@ public final class IOUtilitiesTest extends TestCase {
      */
     @Test
     @DependsOnMethod("testEncodeURI")
+    @SuppressWarnings("deprecation")
     public void testToURI() throws IOException, URISyntaxException {
         assertEquals(new URI("file:/Users/name/Map.png"),
                 IOUtilities.toURI(new URL("file:/Users/name/Map.png"), null));
@@ -169,7 +173,7 @@ public final class IOUtilitiesTest extends TestCase {
     }
 
     /**
-     * Tests the {@link IOUtilities#toFile(URL, String)} method. Do not test a 
Windows-specific path
+     * Tests the {@link IOUtilities#toFile(URL)} method. Do not test a 
Windows-specific path
      * (e.g. {@code "file:///C:/some/path/Map.png"}), since the result is 
different on Windows or
      * Unix platforms.
      *
@@ -193,7 +197,7 @@ public final class IOUtilitiesTest extends TestCase {
     }
 
     /**
-     * Implementation of {@link #testToFile()} using the given encoding.
+     * Implementation of {@link #testToURL()} using the given encoding.
      * If the encoding is null, then the {@code URLDecoder} will not be used.
      *
      * @param  encoding  the encoding, or {@code null} if none.
@@ -202,24 +206,25 @@ public final class IOUtilitiesTest extends TestCase {
      */
     private void testToFile(final String encoding, final String plus) throws 
IOException {
         assertEquals("Unix absolute path.", new File("/Users/name/Map.png"),
-                IOUtilities.toFile(new URL("file:/Users/name/Map.png"), 
encoding));
+                
IOUtilities.toFile(IOUtilities.toURL("file:/Users/name/Map.png", encoding)));
         assertEquals("Path with space.", new File("/Users/name/Map with 
spaces.png"),
-                IOUtilities.toFile(new URL("file:/Users/name/Map with 
spaces.png"), encoding));
+                IOUtilities.toFile(IOUtilities.toURL("file:/Users/name/Map 
with spaces.png", encoding)));
         assertEquals("Path with + sign.", new 
File("/Users/name/++t--++est.shp"),
-                IOUtilities.toFile(new 
URL(CharSequences.replace("file:/Users/name/++t--++est.shp", "+", 
plus).toString()), encoding));
+                IOUtilities.toFile(IOUtilities.toURL(
+                        
CharSequences.replace("file:/Users/name/++t--++est.shp", "+", plus).toString(), 
encoding)));
     }
 
     /**
-     * Tests {@link IOUtilities#toFileOrURL(String, String)}.
+     * Tests {@link IOUtilities#toFileOrURL(String)}.
      *
      * @throws IOException if a URL cannot be parsed.
      */
     @Test
     @DependsOnMethod("testToFileFromUTF8")
     public void testToFileOrURL() throws IOException {
-        assertEquals(new File("/Users/name/Map.png"), 
IOUtilities.toFileOrURL("/Users/name/Map.png", null));
-        assertEquals(new File("/Users/name/Map.png"), 
IOUtilities.toFileOrURL("file:/Users/name/Map.png", null));
-        assertEquals(new URL("http://localhost";),     
IOUtilities.toFileOrURL("http://localhost";, null));
+        assertEquals(new File("/Users/name/Map.png"),        
IOUtilities.toFileOrURL("/Users/name/Map.png", null));
+        assertEquals(new File("/Users/name/Map.png"),        
IOUtilities.toFileOrURL("file:/Users/name/Map.png", null));
+        assertEquals(URI.create("http://localhost";).toURL(), 
IOUtilities.toFileOrURL("http://localhost";, null));
         assertEquals(new File("/Users/name/Map with spaces.png"),
                 
IOUtilities.toFileOrURL("file:/Users/name/Map%20with%20spaces.png", "UTF-8"));
     }
diff --git 
a/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/storage/gpx/WriterTest.java
 
b/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/storage/gpx/WriterTest.java
index f906676612..e37a2266b6 100644
--- 
a/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/storage/gpx/WriterTest.java
+++ 
b/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/storage/gpx/WriterTest.java
@@ -133,6 +133,7 @@ public final class WriterTest extends TestCase {
      * @param version   either {@link StoreProvider#V1_0} or {@link 
StoreProvider#V1_1}.
      * @param expected  name of a test file containing the expected XML result.
      */
+    @SuppressWarnings("deprecation")
     private void testMetadata(final TestData data) throws Exception {
         final Metadata metadata = MetadataTest.create();
         try (WritableStore store = create()) {
@@ -245,6 +246,7 @@ public final class WriterTest extends TestCase {
      * @param store  the store where to write.
      * @param type   the kind of feature to write: way point, route or track.
      */
+    @SuppressWarnings("deprecation")
     private void testFeatures(final WritableStore store, final Type type) 
throws Exception {
         final Types types = store.types;
         /*

Reply via email to