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 805379d32e Update for a change in GeoAPI. The following methods were 
made optional:
805379d32e is described below

commit 805379d32e6ba898b3719cfec7e9e448dfb578c5
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Sat May 11 15:30:52 2024 +0200

    Update for a change in GeoAPI. The following methods were made optional:
    
    - `IdentifiedObject.getRemarks()`
    - `GeneralParameterDescriptor.getDescription()`.
    
    This change required a better handling of optional values in metadata 
`PropertyAccessor`.
---
 .../apache/sis/feature/AbstractIdentifiedType.java | 17 +++++----
 .../org/apache/sis/feature/AbstractOperation.java  |  4 +-
 .../main/org/apache/sis/feature/FeatureFormat.java |  9 +++--
 .../apache/sis/feature/builder/TypeBuilder.java    |  4 +-
 .../sis/feature/DefaultAttributeTypeTest.java      |  5 +--
 .../builder/AssociationRoleBuilderTest.java        |  4 +-
 .../feature/builder/AttributeTypeBuilderTest.java  | 12 +++---
 .../builder/CharacteristicTypeBuilderTest.java     |  4 +-
 .../feature/builder/FeatureTypeBuilderTest.java    |  4 +-
 .../org/apache/sis/metadata/PropertyAccessor.java  |  5 ++-
 .../org/apache/sis/metadata/TypeValuePolicy.java   | 15 ++++----
 .../metadata/iso/lineage/DefaultProcessStep.java   |  2 +-
 .../metadata/simple/SimpleIdentifiedObject.java    |  9 +++--
 .../sis/metadata/simple/SimpleIdentifier.java      |  5 ++-
 .../org/apache/sis/metadata/sql/Dispatcher.java    |  2 +-
 .../apache/sis/metadata/sql/MetadataSource.java    |  2 +-
 .../bind/metadata/replace/QualityParameter.java    | 13 ++++---
 .../bind/metadata/replace/ServiceParameter.java    |  9 +++--
 .../metadata/replace/QualityParameterTest.java     |  4 +-
 .../metadata/replace/ServiceParameterTest.java     |  4 +-
 .../org/apache/sis/xml/test/PackageVerifier.java   |  2 +-
 .../main/org/apache/sis/io/wkt/Formatter.java      |  2 +-
 .../apache/sis/parameter/ParameterTableRow.java    |  6 +--
 .../sis/referencing/AbstractIdentifiedObject.java  | 41 +++++++++-----------
 .../org/apache/sis/referencing/Properties.java     |  4 +-
 .../sis/referencing/internal/DeprecatedCode.java   |  7 ++--
 .../sis/referencing/internal/DeprecatedName.java   |  7 ++--
 .../operation/AbstractCoordinateOperation.java     | 23 +++--------
 .../operation/InverseOperationMethod.java          |  6 +--
 .../operation/LooselyDefinedMethod.java            |  6 +--
 .../operation/TransformedCoordinateSet.java        |  2 +
 .../operation/provider/Mercator1SP.java            |  2 +-
 .../operation/provider/MercatorSpherical.java      |  2 +-
 .../apache/sis/parameter/ParameterBuilderTest.java | 15 ++++----
 .../org/apache/sis/parameter/ParametersTest.java   | 35 ++++++++---------
 .../referencing/AbstractIdentifiedObjectTest.java  | 19 +++++-----
 .../referencing/AbstractReferenceSystemTest.java   |  5 ++-
 .../org/apache/sis/referencing/Assertions.java     | 16 ++++++++
 .../sis/referencing/cs/DefaultCartesianCSTest.java |  3 +-
 .../referencing/cs/DefaultEllipsoidalCSTest.java   |  3 +-
 .../referencing/datum/DefaultEllipsoidTest.java    |  5 ++-
 .../datum/DefaultGeodeticDatumTest.java            | 21 +++++------
 .../datum/DefaultPrimeMeridianTest.java            |  3 +-
 .../datum/DefaultTemporalDatumTest.java            |  3 +-
 .../datum/DefaultVerticalDatumTest.java            |  7 ++--
 .../operation/SingleOperationMarshallingTest.java  |  4 +-
 .../operation/provider/ProvidersTest.java          |  6 +--
 .../report/CoordinateOperationMethods.java         |  2 +-
 .../report/CoordinateReferenceSystems.java         |  4 +-
 .../CC_GeneralOperationParameterTest.java          | 44 +++++++++++-----------
 .../CC_OperationParameterGroupTest.java            |  6 +--
 .../test/org/apache/sis/storage/gpx/TypesTest.java |  2 +-
 .../apache/sis/storage/folder/StoreProvider.java   |  2 +-
 .../main/org/apache/sis/util/Classes.java          | 30 +++++++++++++++
 .../main/org/apache/sis/util/Deprecable.java       |  9 +++--
 geoapi/snapshot                                    |  2 +-
 .../org/apache/sis/gui/dataset/FeatureTable.java   |  2 +-
 57 files changed, 268 insertions(+), 223 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractIdentifiedType.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractIdentifiedType.java
index 8dfe7c05c1..d378c95ada 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractIdentifiedType.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractIdentifiedType.java
@@ -19,6 +19,7 @@ package org.apache.sis.feature;
 import java.util.Map;
 import java.util.Locale;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.logging.Logger;
 import java.io.Serializable;
 import org.opengis.util.NameFactory;
@@ -38,7 +39,7 @@ import org.opengis.feature.IdentifiedType;
  * Identification and description information inherited by property types and 
feature types.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.4
+ * @version 1.5
  * @since   0.5
  */
 public class AbstractIdentifiedType implements IdentifiedType, Deprecable, 
Serializable {
@@ -280,11 +281,11 @@ public class AbstractIdentifiedType implements 
IdentifiedType, Deprecable, Seria
      * Returns a natural language designator for the element.
      * This can be used as an alternative to the {@linkplain #getName() name} 
in user interfaces.
      *
-     * @return natural language designator for the element, or {@code null} if 
none.
+     * @return natural language designator for the element.
      */
     @Override
-    public InternationalString getDesignation() {
-        return designation;
+    public Optional<InternationalString> getDesignation() {
+        return Optional.ofNullable(designation);
     }
 
     /**
@@ -297,8 +298,8 @@ public class AbstractIdentifiedType implements 
IdentifiedType, Deprecable, Seria
      * @return information beyond that required for concise definition of the 
element, or {@code null} if none.
      */
     @Override
-    public InternationalString getDescription() {
-        return description;
+    public Optional<InternationalString> getDescription() {
+        return Optional.ofNullable(description);
     }
 
     /**
@@ -317,8 +318,8 @@ public class AbstractIdentifiedType implements 
IdentifiedType, Deprecable, Seria
      * @since 0.8
      */
     @Override
-    public InternationalString getRemarks() {
-        return deprecated ? description : null;
+    public Optional<InternationalString> getRemarks() {
+        return Optional.ofNullable(deprecated ? description : null);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractOperation.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractOperation.java
index 7a2ef59f1e..2ce24a14b3 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractOperation.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractOperation.java
@@ -145,8 +145,8 @@ public abstract class AbstractOperation extends 
AbstractIdentifiedType implement
         if (properties.isEmpty()) {
             properties.put(NAME_KEY,        super.getName());           // Do 
not invoke user-overrideable method.
             properties.put(DEFINITION_KEY,  super.getDefinition());
-            properties.put(DESIGNATION_KEY, super.getDesignation());
-            properties.put(DESCRIPTION_KEY, super.getDescription());
+            super.getDesignation().ifPresent((i18n) -> 
properties.put(DESIGNATION_KEY, i18n));
+            super.getDescription().ifPresent((i18n) -> 
properties.put(DESCRIPTION_KEY, i18n));
         }
         return properties;
     }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureFormat.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureFormat.java
index 32c9b4d5cb..e9dcaebf52 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureFormat.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureFormat.java
@@ -335,7 +335,7 @@ public class FeatureFormat extends TabularFormat<Object> {
             boolean hasDeprecatedTypes = false;
             for (final PropertyType propertyType : 
featureType.getProperties(true)) {
                 if (!hasDesignation) {
-                    hasDesignation = propertyType.getDesignation() != null;
+                    hasDesignation = propertyType.getDesignation().isPresent();
                 }
                 if (!hasCharacteristics && propertyType instanceof 
AttributeType<?>) {
                     hasCharacteristics = !((AttributeType<?>) 
propertyType).characteristics().isEmpty();
@@ -486,8 +486,9 @@ public class FeatureFormat extends TabularFormat<Object> {
                      * In many cases, this information is not provided and the 
whole column is skipped.
                      */
                     case DESIGNATION: {
-                        final InternationalString d = 
propertyType.getDesignation();
-                        if (d != null) table.append(d.toString(displayLocale));
+                        propertyType.getDesignation().ifPresent((d) -> {
+                            table.append(d.toString(displayLocale));
+                        });
                         break;
                     }
                     /*
@@ -660,7 +661,7 @@ format:                     for (final AttributeType<?> ct 
: ((AttributeType<?>)
                     case REMARKS: {
                         if 
(org.apache.sis.feature.Field.isDeprecated(propertyType)) {
                             
table.append(resources.getString(Vocabulary.Keys.Deprecated));
-                            final InternationalString r = ((Deprecable) 
propertyType).getRemarks();
+                            final InternationalString r = ((Deprecable) 
propertyType).getRemarks().orElse(null);
                             if (r != null) {
                                 remarks.add(r.toString(displayLocale));
                                 appendSuperscript(remarks.size(), table);
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/TypeBuilder.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/TypeBuilder.java
index a139fae082..38914393d7 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/TypeBuilder.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/TypeBuilder.java
@@ -121,8 +121,8 @@ public abstract class TypeBuilder implements Localized {
     final void initialize(final IdentifiedType template) {
         putIfNonNull(AbstractIdentifiedType.NAME_KEY,        
template.getName());
         putIfNonNull(AbstractIdentifiedType.DEFINITION_KEY,  
template.getDefinition());
-        putIfNonNull(AbstractIdentifiedType.DESIGNATION_KEY, 
template.getDesignation());
-        putIfNonNull(AbstractIdentifiedType.DESCRIPTION_KEY, 
template.getDescription());
+        putIfNonNull(AbstractIdentifiedType.DESIGNATION_KEY, 
template.getDesignation().orElse(null));
+        putIfNonNull(AbstractIdentifiedType.DESCRIPTION_KEY, 
template.getDescription().orElse(null));
         if (template instanceof Deprecable && ((Deprecable) 
template).isDeprecated()) {
             identification.put(AbstractIdentifiedType.DEPRECATED_KEY, 
Boolean.TRUE);
         }
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/DefaultAttributeTypeTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/DefaultAttributeTypeTest.java
index de9f152f9f..00fec9751e 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/DefaultAttributeTypeTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/DefaultAttributeTypeTest.java
@@ -127,8 +127,7 @@ public final class DefaultAttributeTypeTest extends 
TestCase {
         assertInstanceOf(LocalName.class, name);
         assertEquals("city", name.toString());
 
-        InternationalString p = city.getDesignation();
-        assertNotNull(p);
+        InternationalString p = city.getDesignation().orElseThrow();
         assertEquals("City",  p.toString(Locale.ENGLISH));
         assertEquals("Ville", p.toString(Locale.FRENCH));
         assertEquals("都市",   p.toString(Locale.JAPANESE));
@@ -138,7 +137,7 @@ public final class DefaultAttributeTypeTest extends 
TestCase {
         assertEquals("Le nom de la ville.", p.toString(Locale.FRENCH));
         assertEquals("都市の名前。", p.toString(Locale.JAPANESE));
 
-        p = city.getDescription();
+        p = city.getDescription().orElseThrow();
         assertEquals("Some verbose description.", p.toString(Locale.ENGLISH));
         assertEquals(String.class, city.getValueClass());
         assertEquals("Utopia",     city.getDefaultValue());
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/AssociationRoleBuilderTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/AssociationRoleBuilderTest.java
index 17142dd7f5..d90e1d783b 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/AssociationRoleBuilderTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/AssociationRoleBuilderTest.java
@@ -54,8 +54,8 @@ public final class AssociationRoleBuilderTest extends 
TestCase {
         final var role = builder.build();
         assertEquals(1, role.getMinimumOccurs());
         assertEquals(2, role.getMaximumOccurs());
-        assertEquals(new SimpleInternationalString("A designation"),          
role.getDesignation());
+        assertEquals(new SimpleInternationalString("A designation"),          
role.getDesignation().orElseThrow());
         assertEquals(new SimpleInternationalString("A definition"),           
role.getDefinition());
-        assertEquals(new SimpleInternationalString("Bridges on the highway"), 
role.getDescription());
+        assertEquals(new SimpleInternationalString("Bridges on the highway"), 
role.getDescription().orElseThrow());
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/AttributeTypeBuilderTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/AttributeTypeBuilderTest.java
index b61fa092c4..4ddd00cee5 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/AttributeTypeBuilderTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/AttributeTypeBuilderTest.java
@@ -66,8 +66,8 @@ public final class AttributeTypeBuilderTest extends TestCase {
         assertEquals(String.class, attribute.getValueClass());
         assertNull(attribute.getDefaultValue());
         assertNull(attribute.getDefinition());
-        assertNull(attribute.getDescription());
-        assertNull(attribute.getDesignation());
+        assertTrue(attribute.getDescription().isEmpty());
+        assertTrue(attribute.getDesignation().isEmpty());
         assertEquals(1, attribute.getMinimumOccurs());
         assertEquals(1, attribute.getMaximumOccurs());
     }
@@ -89,8 +89,8 @@ public final class AttributeTypeBuilderTest extends TestCase {
 
         assertEquals("myScope:myName",      attribute.getName().toString());
         assertEquals("test definition",     
attribute.getDefinition().toString());
-        assertEquals("test description",    
attribute.getDescription().toString());
-        assertEquals("test designation",    
attribute.getDesignation().toString());
+        assertEquals("test description",    
attribute.getDescription().orElseThrow().toString());
+        assertEquals("test designation",    
attribute.getDesignation().orElseThrow().toString());
         assertEquals(String.class,          attribute.getValueClass());
         assertEquals("test default value.", attribute.getDefaultValue());
         assertEquals(10,                    attribute.getMinimumOccurs());
@@ -142,8 +142,8 @@ public final class AttributeTypeBuilderTest extends 
TestCase {
         final var attribute = b2.build();
         assertEquals("temperature",      attribute.getName().toString());
         assertEquals("test definition",  attribute.getDefinition().toString());
-        assertEquals("test description", 
attribute.getDescription().toString());
-        assertEquals("test designation", 
attribute.getDesignation().toString());
+        assertEquals("test description", 
attribute.getDescription().orElseThrow().toString());
+        assertEquals("test designation", 
attribute.getDesignation().orElseThrow().toString());
         assertEquals(Double.class,       attribute.getValueClass());
         assertEquals(Double.valueOf(25), attribute.getDefaultValue());
     }
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/CharacteristicTypeBuilderTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/CharacteristicTypeBuilderTest.java
index d9dd845dfa..0da391bec6 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/CharacteristicTypeBuilderTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/CharacteristicTypeBuilderTest.java
@@ -81,8 +81,8 @@ public final class CharacteristicTypeBuilderTest extends 
TestCase {
         final var attribute = b2.build();
         assertEquals("stddev",           attribute.getName().toString());
         assertEquals("test definition",  attribute.getDefinition().toString());
-        assertEquals("test description", 
attribute.getDescription().toString());
-        assertEquals("test designation", 
attribute.getDesignation().toString());
+        assertEquals("test description", 
attribute.getDescription().orElseThrow().toString());
+        assertEquals("test designation", 
attribute.getDesignation().orElseThrow().toString());
         assertEquals(Float.class,        attribute.getValueClass());
         assertEquals(Float.valueOf(2),   attribute.getDefaultValue());
     }
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java
index 9053be2060..3e4f9bb389 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java
@@ -137,8 +137,8 @@ public final class FeatureTypeBuilderTest extends TestCase {
         final var feature = builder.build();
         assertEquals("myScope:myName",   feature.getName().toString());
         assertEquals("test definition",  feature.getDefinition().toString());
-        assertEquals("test description", feature.getDescription().toString());
-        assertEquals("test designation", feature.getDesignation().toString());
+        assertEquals("test description", 
feature.getDescription().orElseThrow().toString());
+        assertEquals("test designation", 
feature.getDesignation().orElseThrow().toString());
         assertTrue  (                    feature.isAbstract());
 
         final var it = feature.getProperties(true).iterator();
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/PropertyAccessor.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/PropertyAccessor.java
index c80e2d79f7..45382ab1a4 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/PropertyAccessor.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/PropertyAccessor.java
@@ -348,7 +348,7 @@ class PropertyAccessor {
              * is to get a type which can be accepted by the setter.
              */
             Class<?> elementType = getter.getReturnType();
-            if (Collection.class.isAssignableFrom(elementType)) {
+            if (Collection.class.isAssignableFrom(elementType) || 
Classes.isParameterizedProperty(elementType)) {
                 elementType = Classes.boundOfParameterizedProperty(getter);
                 if (elementType == null) {
                     // Subclass has erased parameterized type. Use method 
declared in the interface.
@@ -595,7 +595,8 @@ class PropertyAccessor {
                     return elementTypes[index];
                 }
                 case PROPERTY_TYPE: {
-                    return getters[index].getReturnType();
+                    final Class<?> type = getters[index].getReturnType();
+                    return Classes.isParameterizedProperty(type) ? 
elementTypes[index] : type;
                 }
                 case DECLARING_INTERFACE: {
                     return getters[index].getDeclaringClass();
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TypeValuePolicy.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TypeValuePolicy.java
index 193e2c8ef0..179b703a64 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TypeValuePolicy.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TypeValuePolicy.java
@@ -31,19 +31,20 @@ package org.apache.sis.metadata;
  */
 public enum TypeValuePolicy {
     /**
-     * The type of a property, as inferred from the
-     * {@linkplain java.lang.reflect.Method#getReturnType() return type} of 
the property method
-     * defined in the interface.
+     * The type of a property as inferred from the return type of the property 
method defined in the interface.
+     * The property type is the {@linkplain 
java.lang.reflect.Method#getReturnType()} except for the following
+     * special cases:
      *
-     * <h4>Notes</h4>
      * <ul>
-     *   <li>Collections are not handled in any special way: if the return 
type is a collection,
-     *       then the property type is {@code Collection.class} or any other 
declared return type.</li>
-     *   <li>As a special case, values of type {@code double} (the primitive 
type) in
+     *   <li>{@link java.util.Optional} are replaced by the wrapped type.</li>
+     *   <li>Values of type {@code double} (the primitive type) in
      *       {@link org.opengis.metadata.extent.GeographicBoundingBox} are 
wrapped in
      *       {@link org.apache.sis.measure.Longitude} and {@link 
org.apache.sis.measure.Latitude}
      *       objects instead of {@link Double}.</li>
      * </ul>
+     *
+     * Note that collections are not handled in any special way. If the return 
type is a collection,
+     * then the property type is {@code Collection.class} or any other 
declared return type.
      */
     PROPERTY_TYPE,
 
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/lineage/DefaultProcessStep.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/lineage/DefaultProcessStep.java
index 4442c0643c..a44ec542cf 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/lineage/DefaultProcessStep.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/lineage/DefaultProcessStep.java
@@ -186,7 +186,7 @@ public class DefaultProcessStep extends ISOMetadata 
implements ProcessStep {
         if (object != null) {
             description           = object.getDescription();
             rationale             = object.getRationale();
-            stepDateTime          = 
TemporalUtilities.createInstant(object.getDate());
+            stepDateTime          = object.getStepDateTime();
             processors            = copyCollection(object.getProcessors(), 
Responsibility.class);
             references            = copyCollection(object.getReferences(), 
Citation.class);
             sources               = copyCollection(object.getSources(), 
Source.class);
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleIdentifiedObject.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleIdentifiedObject.java
index 5063035c46..32cb2e0d51 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleIdentifiedObject.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleIdentifiedObject.java
@@ -17,6 +17,7 @@
 package org.apache.sis.metadata.simple;
 
 import java.util.Objects;
+import java.util.Optional;
 import java.io.Serializable;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.Identifier;
@@ -88,12 +89,12 @@ public class SimpleIdentifiedObject implements 
IdentifiedObject, LenientComparab
      * Returns a narrative explanation of the role of this object.
      * The default implementation returns {@link Identifier#getDescription()}.
      *
-     * @return a narrative explanation of the role of this object, or {@code 
null} if none.
+     * @return a narrative explanation of the role of this object.
      */
-    public InternationalString getDescription() {
+    public Optional<InternationalString> getDescription() {
         @SuppressWarnings("LocalVariableHidesMemberVariable")
         final Identifier name = this.name;
-        return (name != null) ? name.getDescription() : null;
+        return Optional.ofNullable((name != null) ? name.getDescription() : 
null);
     }
 
     /**
@@ -145,7 +146,7 @@ public class SimpleIdentifiedObject implements 
IdentifiedObject, LenientComparab
                 return Objects.equals(getName(), that.getName()) &&
                         isNullOrEmpty(that.getIdentifiers()) &&
                         isNullOrEmpty(that.getAlias()) &&
-                        that.getRemarks() == null;
+                        that.getRemarks().isEmpty();
             }
         }
         return false;
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleIdentifier.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleIdentifier.java
index 37cd2a7336..d94fc4a74b 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleIdentifier.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleIdentifier.java
@@ -17,6 +17,7 @@
 package org.apache.sis.metadata.simple;
 
 import java.util.Objects;
+import java.util.Optional;
 import java.io.Serializable;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.citation.Citation;
@@ -141,8 +142,8 @@ public class SimpleIdentifier implements Identifier, 
Deprecable, Serializable {
      * An optional free text.
      */
     @Override
-    public InternationalString getRemarks() {
-        return null;
+    public Optional<InternationalString> getRemarks() {
+        return Optional.empty();
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/Dispatcher.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/Dispatcher.java
index caf5f563c7..d329d38869 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/Dispatcher.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/Dispatcher.java
@@ -289,7 +289,7 @@ final class Dispatcher implements InvocationHandler {
      */
     final String error(final Method method) {
         Class<?> returnType = method.getReturnType();
-        if (Collection.class.isAssignableFrom(returnType)) {
+        if (Classes.isParameterizedProperty(returnType) || 
Collection.class.isAssignableFrom(returnType)) {
             final Class<?> elementType = 
Classes.boundOfParameterizedProperty(method);
             if (elementType != null) {
                 returnType = elementType;
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataSource.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataSource.java
index 49ad8d16ea..b1e7fff38f 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataSource.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataSource.java
@@ -985,7 +985,7 @@ public class MetadataSource implements AutoCloseable {
         final Class<?> type           = 
TableHierarchy.subType(info.getMetadataType(), toSearch.identifier);
         final Class<?> returnType     = method.getReturnType();
         final boolean  wantCollection = 
Collection.class.isAssignableFrom(returnType);
-        final Class<?> elementType    = wantCollection ? 
Classes.boundOfParameterizedProperty(method) : returnType;
+        final Class<?> elementType    = (wantCollection || 
Classes.isParameterizedProperty(returnType)) ? 
Classes.boundOfParameterizedProperty(method) : returnType;
         final boolean  isMetadata     = standard.isMetadata(elementType);
         final String   tableName      = getTableName(type);
         final String   columnName     = 
info.asNameMap(standard).get(method.getName());
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/QualityParameter.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/QualityParameter.java
index 1fd5525f30..b79fd3a168 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/QualityParameter.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/QualityParameter.java
@@ -17,6 +17,7 @@
 package org.apache.sis.xml.bind.metadata.replace;
 
 import java.util.Map;
+import java.util.Optional;
 import jakarta.xml.bind.annotation.XmlType;
 import jakarta.xml.bind.annotation.XmlElement;
 import jakarta.xml.bind.annotation.XmlRootElement;
@@ -137,10 +138,9 @@ public final class QualityParameter extends Parameter {
             code = id.getCode();
             definition = id.getDescription();
         }
-        InternationalString text = parameter.getDescription();
-        if (text != null) {
+        parameter.getDescription().ifPresent((text) -> {
             description = new DefaultMeasureDescription(text);
-        }
+        });
         valueType = parameter.getValueType();
         valueStructure = 
ValueStructure.valueOf(parameter.getValueClass()).orElse(null);
     }
@@ -178,12 +178,13 @@ public final class QualityParameter extends Parameter {
     /**
      * Returns a narrative explanation of the role of the parameter.
      *
-     * @return a narrative explanation of the role of the parameter, or {@code 
null} if none.
+     * @return a narrative explanation of the role of the parameter.
      */
     @Override
-    public InternationalString getDescription() {
+    public Optional<InternationalString> getDescription() {
+        @SuppressWarnings("LocalVariableHidesMemberVariable")
         final Description description = this.description;
-        return (description != null) ? description.getTextDescription() : null;
+        return Optional.ofNullable((description != null) ? 
description.getTextDescription() : null);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/ServiceParameter.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/ServiceParameter.java
index 91addb98ec..b84bcb2473 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/ServiceParameter.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/ServiceParameter.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.xml.bind.metadata.replace;
 
+import java.util.Optional;
 import jakarta.xml.bind.annotation.XmlType;
 import jakarta.xml.bind.annotation.XmlElement;
 import jakarta.xml.bind.annotation.XmlRootElement;
@@ -160,7 +161,7 @@ public final class ServiceParameter extends Parameter {
         super(parameter);
         memberName    = getMemberName(parameter);
         direction     = parameter.getDirection();
-        description   = parameter.getDescription();
+        description   = parameter.getDescription().orElse(null);
         optionality   = parameter.getMinimumOccurs() > 0;
         repeatability = parameter.getMaximumOccurs() > 1;
     }
@@ -335,11 +336,11 @@ public final class ServiceParameter extends Parameter {
     /**
      * Returns a narrative explanation of the role of the parameter.
      *
-     * @return a narrative explanation of the role of the parameter, or {@code 
null} if none.
+     * @return a narrative explanation of the role of the parameter.
      */
     @Override
-    public InternationalString getDescription() {
-        return description;
+    public Optional<InternationalString> getDescription() {
+        return Optional.ofNullable(description);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/metadata/replace/QualityParameterTest.java
 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/metadata/replace/QualityParameterTest.java
index 28fe198a2a..6ea99d0656 100644
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/metadata/replace/QualityParameterTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/metadata/replace/QualityParameterTest.java
@@ -70,8 +70,8 @@ public final class QualityParameterTest extends TestCase {
         final Identifier name = param.getName();
         assertNull  (name.getCodeSpace());
         assertEquals("some parameter", name.getCode());
-        assertEquals("a definition",  String.valueOf(name .getDescription()));
-        assertEquals("a description", String.valueOf(param.getDescription()));
+        assertEquals("a definition",   name.getDescription().toString());
+        assertEquals("a description",  
param.getDescription().orElseThrow().toString());
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/metadata/replace/ServiceParameterTest.java
 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/metadata/replace/ServiceParameterTest.java
index aa31385b02..4d67ae3516 100644
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/metadata/replace/ServiceParameterTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/metadata/replace/ServiceParameterTest.java
@@ -69,8 +69,8 @@ public final class ServiceParameterTest extends TestCase {
         final Identifier name = param.getName();
         assertEquals("TestSpace", name.getCodeSpace());
         assertEquals("My service parameter", name.getCode());
-        assertEquals("TestSpace:My service parameter", String.valueOf(name));
-        assertNull  (param.getDescription());
+        assertEquals("TestSpace:My service parameter", name.toString());
+        assertTrue(param.getDescription().isEmpty());
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/PackageVerifier.java
 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/PackageVerifier.java
index 50a40914b6..90a3185bdd 100644
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/PackageVerifier.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/PackageVerifier.java
@@ -353,7 +353,7 @@ final class PackageVerifier {
         for (final Method method : type.getDeclaredMethods()) {
             Class<?> valueType = method.getReturnType();
             final boolean isCollection = 
Collection.class.isAssignableFrom(valueType);
-            if (isCollection) {
+            if (isCollection || Classes.isParameterizedProperty(valueType)) {
                 valueType = Classes.boundOfParameterizedProperty(method);
             }
             verify(method, method.getName(), valueType, isCollection);
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
index 9466f65af7..085031fb9c 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
@@ -871,7 +871,7 @@ public class Formatter implements Localized {
             }
         }
         if (showRemarks) {
-            appendOnNewLine(WKTKeywords.Remark, object.getRemarks(), 
ElementKind.REMARKS);
+            appendOnNewLine(WKTKeywords.Remark, 
object.getRemarks().orElse(null), ElementKind.REMARKS);
         }
         isComplement = false;
     }
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/ParameterTableRow.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/ParameterTableRow.java
index 583077a13e..24943bf64d 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/ParameterTableRow.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/ParameterTableRow.java
@@ -31,7 +31,6 @@ import java.text.Format;
 import java.text.FieldPosition;
 import javax.measure.Unit;
 import org.opengis.util.GenericName;
-import org.opengis.util.InternationalString;
 import org.opengis.metadata.Identifier;
 import org.opengis.referencing.IdentifiedObject;
 import org.apache.sis.io.wkt.Colors;
@@ -183,12 +182,11 @@ final class ParameterTableRow {
         /*
          * Take the remarks, if any.
          */
-        final InternationalString r = object.getRemarks();
-        if (r != null) {
+        object.getRemarks().ifPresent((r) -> {
             final int n = remarks.size() + 1;
             final Integer p = remarks.putIfAbsent(r.toString(locale), n);
             this.remarks = (p != null) ? p : n;
-        }
+        });
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AbstractIdentifiedObject.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AbstractIdentifiedObject.java
index 00cbf9c294..679b1ed56e 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AbstractIdentifiedObject.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AbstractIdentifiedObject.java
@@ -24,6 +24,7 @@ import java.util.AbstractCollection;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Formattable;
 import java.util.FormattableFlags;
 import java.util.function.Function;
@@ -129,7 +130,7 @@ import org.opengis.referencing.ObjectDomain;
  * @since   0.4
  */
 @XmlType(name = "IdentifiedObjectType", propOrder = {
-    "description",
+    "descriptionGML",
     "identifier",
     "names",
     "remarks",
@@ -236,6 +237,7 @@ public class AbstractIdentifiedObject extends 
FormattableObject implements Ident
      * @see #getRemarks()
      */
     @SuppressWarnings("serial")         // Most SIS implementations are 
serializable.
+    @XmlElement(name = "remarks")
     private InternationalString remarks;
 
     /**
@@ -454,7 +456,7 @@ public class AbstractIdentifiedObject extends 
FormattableObject implements Ident
         alias       = nonEmpty(object.getAlias()); // Favor null for empty set 
in case it is not Collections.EMPTY_SET
         identifiers = nonEmpty(object.getIdentifiers());
         domains     = nonEmpty(object.getDomains());
-        remarks     =          object.getRemarks();
+        remarks     =          object.getRemarks().orElse(null);
         deprecated  = (object instanceof Deprecable) ? ((Deprecable) 
object).isDeprecated() : false;
     }
 
@@ -575,15 +577,14 @@ public class AbstractIdentifiedObject extends 
FormattableObject implements Ident
      * The default implementation returns the {@linkplain 
ImmutableIdentifier#getDescription() description}
      * provided by this object's {@linkplain #getName() name}.
      *
-     * @return a narrative explanation of the role of this object, or {@code 
null} if none.
+     * @return a narrative explanation of the role of this object.
      *
      * @see ImmutableIdentifier#getDescription()
      *
      * @since 0.6
      */
-    @XmlElement(name = "description")
-    public InternationalString getDescription() {
-        return (name != null) ? name.getDescription() : null;
+    public Optional<InternationalString> getDescription() {
+        return Optional.ofNullable((name != null) ? name.getDescription() : 
null);
     }
 
     /**
@@ -591,12 +592,11 @@ public class AbstractIdentifiedObject extends 
FormattableObject implements Ident
      * If this object {@linkplain #isDeprecated() is deprecated}, then the 
remarks should give
      * indication about the replacement (e.g. <q>superceded by …</q>).
      *
-     * @return the remarks, or {@code null} if none.
+     * @return the remarks.
      */
     @Override
-    @XmlElement(name = "remarks")
-    public InternationalString getRemarks() {
-        return remarks;
+    public Optional<InternationalString> getRemarks() {
+        return Optional.ofNullable(remarks);
     }
 
     /**
@@ -1179,6 +1179,14 @@ public class AbstractIdentifiedObject extends 
FormattableObject implements Ident
         }
     }
 
+    /**
+     * Returns a narrative explanation of the role of this object.
+     */
+    @XmlElement(name = "description")
+    private InternationalString getDescriptionGML() {
+        return getDescription().orElse(null);
+    }
+
     /**
      * Finds the first non-null domain element.
      *
@@ -1250,19 +1258,6 @@ public class AbstractIdentifiedObject extends 
FormattableObject implements Ident
         domains = Collections.singleton(new DefaultObjectDomain(value, area));
     }
 
-    /**
-     * Invoked by JAXB for setting the remarks.
-     *
-     * @see #getRemarks()
-     */
-    private void setRemarks(final InternationalString value) {
-        if (remarks == null) {
-            remarks = value;
-        } else {
-            propertyAlreadySet("setRemarks", "remarks");
-        }
-    }
-
     /**
      * Logs a warning saying that an unmarshalled property was already set.
      *
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/Properties.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/Properties.java
index 289b12216a..8976492e8d 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/Properties.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/Properties.java
@@ -134,7 +134,7 @@ final class Properties extends AbstractMap<String,Object> 
implements Serializabl
                 case 1: return toArray(object.getIdentifiers(), 
Identifier[]::new);     // IDENTIFIERS_KEY
                 case 2: return toArray(object.getAlias(),      
GenericName[]::new);     // ALIAS_KEY
                 case 3: return toArray(object.getDomains(),   
ObjectDomain[]::new);     // DOMAINS_KEY
-                case 4: return         object.getRemarks();                    
         // REMARKS_KEY
+                case 4: return         object.getRemarks().orElse(null);       
         // REMARKS_KEY
                 case 5: {   // SCOPE_KEY
                     for (final ObjectDomain domain : object.getDomains()) {
                         InternationalString scope = domain.getScope();
@@ -151,7 +151,7 @@ final class Properties extends AbstractMap<String,Object> 
implements Serializabl
                 }
                 case 7: {   // OPERATION_VERSION_KEY
                     if (object instanceof CoordinateOperation) {
-                        return ((CoordinateOperation) 
object).getOperationVersion();
+                        return ((CoordinateOperation) 
object).getOperationVersion().orElse(null);
                     }
                     break;
                 }
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/DeprecatedCode.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/DeprecatedCode.java
index 54cb6629bc..872a241491 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/DeprecatedCode.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/DeprecatedCode.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.referencing.internal;
 
+import java.util.Optional;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.citation.Citation;
 import org.apache.sis.referencing.ImmutableIdentifier;
@@ -77,11 +78,11 @@ public final class DeprecatedCode extends 
ImmutableIdentifier implements Depreca
      *
      * <div class="note"><b>Example:</b> "superseded by code XYZ".</div>
      *
-     * @return information about the replacement for this identifier, or 
{@code null} if none.
+     * @return information about the replacement for this identifier.
      */
     @Override
-    public InternationalString getRemarks() {
-        return super.getDescription();
+    public Optional<InternationalString> getRemarks() {
+        return Optional.ofNullable(super.getDescription());
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/DeprecatedName.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/DeprecatedName.java
index d4b25f677f..5ab841d0c7 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/DeprecatedName.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/DeprecatedName.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.referencing.internal;
 
+import java.util.Optional;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.citation.Citation;
 import org.apache.sis.referencing.NamedIdentifier;
@@ -69,11 +70,11 @@ public final class DeprecatedName extends NamedIdentifier 
implements Deprecable
      *
      * <div class="note"><b>Example:</b> "superseded by code XYZ".</div>
      *
-     * @return information about the replacement for this name, or {@code 
null} if none.
+     * @return information about the replacement for this name.
      */
     @Override
-    public InternationalString getRemarks() {
-        return super.getDescription();
+    public Optional<InternationalString> getRemarks() {
+        return Optional.ofNullable(super.getDescription());
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
index 00b10374aa..038e256a13 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
@@ -178,6 +178,7 @@ public class AbstractCoordinateOperation extends 
AbstractIdentifiedObject implem
      *
      * @see #getOperationVersion()
      */
+    @XmlElement(name = "operationVersion")
     private String operationVersion;
 
     /**
@@ -419,7 +420,7 @@ check:      for (int isTarget=0; ; isTarget++) {        // 
0 == source check; 1
         sourceCRS                   = operation.getSourceCRS();
         targetCRS                   = operation.getTargetCRS();
         interpolationCRS            = 
operation.getInterpolationCRS().orElse(null);
-        operationVersion            = operation.getOperationVersion();
+        operationVersion            = 
operation.getOperationVersion().orElse(null);
         coordinateOperationAccuracy = 
operation.getCoordinateOperationAccuracy();
         transform                   = operation.getMathTransform();
         if (operation instanceof AbstractCoordinateOperation) {
@@ -552,12 +553,11 @@ check:      for (int isTarget=0; ; isTarget++) {        
// 0 == source check; 1
      * nature of the parameters. In principle this property is irrelevant to 
coordinate
      * {@linkplain DefaultConversion conversions}, but Apache SIS accepts it 
anyway.
      *
-     * @return the coordinate operation version, or {@code null} in none.
+     * @return the coordinate operation version.
      */
     @Override
-    @XmlElement(name = "operationVersion")
-    public String getOperationVersion() {
-        return operationVersion;
+    public Optional<String> getOperationVersion() {
+        return Optional.ofNullable(operationVersion);
     }
 
     /**
@@ -1162,19 +1162,6 @@ check:      for (int isTarget=0; ; isTarget++) {        
// 0 == source check; 1
         }
     }
 
-    /**
-     * Invoked by JAXB only at unmarshalling time.
-     *
-     * @see #getOperationVersion()
-     */
-    private void setOperationVersion(final String value) {
-        if (operationVersion == null) {
-            operationVersion = value;
-        } else {
-            
ImplementationHelper.propertyAlreadySet(AbstractCoordinateOperation.class, 
"setOperationVersion", "operationVersion");
-        }
-    }
-
     /**
      * Invoked by JAXB after unmarshalling.
      * May be overridden by subclasses.
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/InverseOperationMethod.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/InverseOperationMethod.java
index 2ada34c583..808f81cc25 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/InverseOperationMethod.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/InverseOperationMethod.java
@@ -95,7 +95,7 @@ final class InverseOperationMethod extends 
DefaultOperationMethod {
         }
         boolean useSameParameters = false;
         for (final GeneralParameterDescriptor descriptor : 
method.getParameters().descriptors()) {
-            useSameParameters = (descriptor.getRemarks() instanceof 
SignReversalComment);
+            useSameParameters = (descriptor.getRemarks().orElse(null) 
instanceof SignReversalComment);
             if (!useSameParameters) break;
         }
         if (useSameParameters) {
@@ -106,7 +106,7 @@ final class InverseOperationMethod extends 
DefaultOperationMethod {
         final Map<String,Object> properties = new HashMap<>(6);
         properties.put(NAME_KEY,    name);
         properties.put(FORMULA_KEY, method.getFormula());
-        properties.put(REMARKS_KEY, method.getRemarks());
+        properties.put(REMARKS_KEY, method.getRemarks().orElse(null));
         if (method instanceof Deprecable) {
             properties.put(DEPRECATED_KEY, ((Deprecable) 
method).isDeprecated());
         }
@@ -157,7 +157,7 @@ final class InverseOperationMethod extends 
DefaultOperationMethod {
                 final Object value = src.getValue();
                 if (value instanceof Number) {
                     final ParameterDescriptor<?> descriptor = 
src.getDescriptor();
-                    final InternationalString remarks = 
descriptor.getRemarks();
+                    final InternationalString remarks = 
descriptor.getRemarks().orElse(null);
                     if (remarks != SignReversalComment.SAME) {
                         if (remarks != SignReversalComment.OPPOSITE) {
                             /*
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/LooselyDefinedMethod.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/LooselyDefinedMethod.java
index e60f56ebad..ce9430289b 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/LooselyDefinedMethod.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/LooselyDefinedMethod.java
@@ -65,13 +65,13 @@ final class LooselyDefinedMethod {
     static final DefaultOperationMethod AFFINE_GEOCENTRIC;
 
     static {
-        final HashMap<String,Object> properties = new HashMap<>(4);
+        final var properties = new HashMap<String,Object>(4);
         properties.put(DefaultOperationMethod.NAME_KEY,    "Undefined 
parameters");
         properties.put(DefaultOperationMethod.REMARKS_KEY, "Placeholder for 
what should be a chain of coordinate operations.");
-        final DefaultParameterDescriptorGroup parameters = new 
DefaultParameterDescriptorGroup(properties, 0, 1);
+        final var parameters = new DefaultParameterDescriptorGroup(properties, 
0, 1);
 
         properties.put(DefaultOperationMethod.NAME_KEY,    "Affine parametric 
transformation in geocentric domain");
-        properties.put(DefaultOperationMethod.REMARKS_KEY, 
parameters.getRemarks());
+        properties.put(DefaultOperationMethod.REMARKS_KEY, 
parameters.getRemarks().get());
         properties.put(DefaultOperationMethod.FORMULA_KEY, new DefaultFormula(
                 "This operation method is currently an implementation 
dependent black box. " +
                 "A future version may redefine this method in terms of more 
standard methods."));
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/TransformedCoordinateSet.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/TransformedCoordinateSet.java
index 373f089828..c841441ce6 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/TransformedCoordinateSet.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/TransformedCoordinateSet.java
@@ -43,6 +43,8 @@ import org.opengis.coordinate.CoordinateMetadata;
  * The result of transforming coordinate tuples using the math transform of a 
given coordinate operation.
  *
  * @author  Martin Desruisseaux (Geomatys)
+ *
+ * @todo The current implementation is inefficient.
  */
 final class TransformedCoordinateSet extends AbstractCoordinateSet implements 
UnaryOperator<DirectPosition> {
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Mercator1SP.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Mercator1SP.java
index 0053c66d09..0f8ffa58e0 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Mercator1SP.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Mercator1SP.java
@@ -110,7 +110,7 @@ public final class Mercator1SP extends AbstractMercator {
         LATITUDE_OF_ORIGIN = 
createZeroConstant(builder.addNamesAndIdentifiers(Equirectangular.LATITUDE_OF_ORIGIN)
                 .reidentify(Citations.GEOTIFF, "3081")
                 .rename(Citations.GEOTIFF, "NatOriginLat")
-                .setRemarks(Equirectangular.LATITUDE_OF_ORIGIN.getRemarks()));
+                
.setRemarks(Equirectangular.LATITUDE_OF_ORIGIN.getRemarks().get()));
 
         LONGITUDE_OF_ORIGIN = 
createLongitude(builder.addNamesAndIdentifiers(Equirectangular.LONGITUDE_OF_ORIGIN)
                 .reidentify(Citations.GEOTIFF, "3080")
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/MercatorSpherical.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/MercatorSpherical.java
index 079d290174..8aa5cf1a90 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/MercatorSpherical.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/MercatorSpherical.java
@@ -59,7 +59,7 @@ public final class MercatorSpherical extends AbstractMercator 
{
          */
         final ParameterDescriptor<Double> scaleFactor = createScale(builder
                 .addNamesAndIdentifiers(Mercator1SP.SCALE_FACTOR)
-                .setRemarks(Mercator2SP.SCALE_FACTOR.getRemarks())
+                .setRemarks(Mercator2SP.SCALE_FACTOR.getRemarks().get())
                 .setRequired(false));
 
         PARAMETERS = addNameAndLegacy(addIdentifierAndLegacy(builder, 
IDENTIFIER, "9841"),
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParameterBuilderTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParameterBuilderTest.java
index 218cccca94..e523800f4d 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParameterBuilderTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParameterBuilderTest.java
@@ -24,6 +24,7 @@ import org.apache.sis.measure.Units;
 // Test dependencies
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
+import static org.apache.sis.referencing.Assertions.assertRemarksEquals;
 import org.apache.sis.test.TestCase;
 
 
@@ -86,14 +87,14 @@ public final class ParameterBuilderTest extends TestCase {
             builder.addName("False northing")                 .create(0, 
Units.METRE)
         };
         // Tests random properties.
-        assertEquals("EPSG",             
parameters[1].getName().getCodeSpace());
-        assertEquals("False easting",    parameters[3].getName().getCode());
-        assertEquals("Some remarks.",    
parameters[0].getRemarks().toString());
-        assertEquals(Double.valueOf(84), parameters[1].getMaximumValue());
-        assertEquals(Units.METRE,        parameters[4].getUnit());
-        assertTrue  (                    parameters[1].getAlias().isEmpty());
+        assertEquals("EPSG",                 
parameters[1].getName().getCodeSpace());
+        assertEquals("False easting",        
parameters[3].getName().getCode());
+        assertRemarksEquals("Some remarks.", parameters[0], null);
+        assertEquals(Double.valueOf(84),     parameters[1].getMaximumValue());
+        assertEquals(Units.METRE,            parameters[4].getUnit());
+        assertTrue  (                        
parameters[1].getAlias().isEmpty());
 
-        final GenericName alias = parameters[0].getAlias().iterator().next();
+        GenericName alias = parameters[0].getAlias().iterator().next();
         assertEquals("central_meridian",     alias.tip().toString());
         assertEquals("OGC",                  alias.head().toString());
         assertEquals("OGC:central_meridian", alias.toString());
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParametersTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParametersTest.java
index e000806703..82190ee516 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParametersTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParametersTest.java
@@ -19,6 +19,7 @@ package org.apache.sis.parameter;
 import java.util.Map;
 import java.util.Set;
 import java.util.Collection;
+import java.util.Optional;
 import javax.measure.Unit;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterValue;
@@ -109,23 +110,23 @@ public final class ParametersTest extends TestCase {
     {
         assertEquals(valueDomain, Parameters.getValueDomain(descriptor));
         assertEquals(valueDomain, Parameters.getValueDomain(new 
ParameterDescriptor<T>() {
-            @Override public Identifier               getName()          
{return descriptor.getName();}
-            @Override public Collection<GenericName>  getAlias()         
{return descriptor.getAlias();}
-            @Override public Set<Identifier>          getIdentifiers()   
{return descriptor.getIdentifiers();}
-            @Override public InternationalString      getRemarks()       
{return descriptor.getRemarks();}
-            @Override public InternationalString      getDescription()   
{return descriptor.getDescription();}
-            @Override public ParameterDirection       getDirection()     
{return descriptor.getDirection();}
-            @Override public int                      getMinimumOccurs() 
{return descriptor.getMinimumOccurs();}
-            @Override public int                      getMaximumOccurs() 
{return descriptor.getMaximumOccurs();}
-            @Override public TypeName                 getValueType()     
{return descriptor.getValueType();}
-            @Override public Class<T>                 getValueClass()    
{return descriptor.getValueClass();}
-            @Override public Set<T>                   getValidValues()   
{return descriptor.getValidValues();}
-            @Override public Comparable<T>            getMinimumValue()  
{return descriptor.getMinimumValue();}
-            @Override public Comparable<T>            getMaximumValue()  
{return descriptor.getMaximumValue();}
-            @Override public T                        getDefaultValue()  
{return descriptor.getDefaultValue();}
-            @Override public Unit<?>                  getUnit()          
{return descriptor.getUnit();}
-            @Override public ParameterValue<T>        createValue()      
{return descriptor.createValue();}
-            @Override public String                   toWKT()            
{return descriptor.toWKT();}
+            @Override public Identifier                    getName()          
{return descriptor.getName();}
+            @Override public Collection<GenericName>       getAlias()         
{return descriptor.getAlias();}
+            @Override public Set<Identifier>               getIdentifiers()   
{return descriptor.getIdentifiers();}
+            @Override public Optional<InternationalString> getRemarks()       
{return descriptor.getRemarks();}
+            @Override public Optional<InternationalString> getDescription()   
{return descriptor.getDescription();}
+            @Override public ParameterDirection            getDirection()     
{return descriptor.getDirection();}
+            @Override public int                           getMinimumOccurs() 
{return descriptor.getMinimumOccurs();}
+            @Override public int                           getMaximumOccurs() 
{return descriptor.getMaximumOccurs();}
+            @Override public TypeName                      getValueType()     
{return descriptor.getValueType();}
+            @Override public Class<T>                      getValueClass()    
{return descriptor.getValueClass();}
+            @Override public Set<T>                        getValidValues()   
{return descriptor.getValidValues();}
+            @Override public Comparable<T>                 getMinimumValue()  
{return descriptor.getMinimumValue();}
+            @Override public Comparable<T>                 getMaximumValue()  
{return descriptor.getMaximumValue();}
+            @Override public T                             getDefaultValue()  
{return descriptor.getDefaultValue();}
+            @Override public Unit<?>                       getUnit()          
{return descriptor.getUnit();}
+            @Override public ParameterValue<T>             createValue()      
{return descriptor.createValue();}
+            @Override public String                        toWKT()            
{return descriptor.toWKT();}
         }));
     }
 
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java
index e0aa392a90..44798340d0 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java
@@ -35,6 +35,7 @@ import org.apache.sis.test.TestCase;
 import static org.apache.sis.test.Assertions.assertMessageContains;
 import static org.apache.sis.test.Assertions.assertSerializedEquals;
 import static org.apache.sis.test.TestUtilities.getSingleton;
+import static org.apache.sis.referencing.Assertions.assertRemarksEquals;
 
 // Specific to the geoapi-4.0 branch:
 import org.opengis.metadata.Identifier;
@@ -83,15 +84,15 @@ public final class AbstractIdentifiedObjectTest extends 
TestCase {
     {
         Validators.validate(object);
         final var name = object.getName();
-        assertEquals("GRS 1980",                      name.getCode(), "name");
-        assertEquals("EPSG",                          name.getCodeSpace(), 
"codespace");
-        assertEquals("8.3",                           name.getVersion(), 
"version");
-        assertEquals("International 1979",            
getSingleton(object.getAlias()).toString(), "aliases");
-        assertEquals(name,                            
getSingleton(object.getNames()), "names");
-        assertEquals(identifiers,                     object.getIdentifiers(), 
"identifiers");
-        assertEquals(gmlID,                           object.getID(), "ID");
-        assertEquals("Adopted by IUGG 1979 Canberra", 
object.getRemarks().toString(Locale.ENGLISH), "remarks");
-        assertEquals("Adopté par IUGG 1979 Canberra", 
object.getRemarks().toString(Locale.FRENCH), "remarks_fr");
+        assertEquals("GRS 1980",           name.getCode(), "name");
+        assertEquals("EPSG",               name.getCodeSpace(), "codespace");
+        assertEquals("8.3",                name.getVersion(), "version");
+        assertEquals("International 1979", 
getSingleton(object.getAlias()).toString(), "aliases");
+        assertEquals(name,                 getSingleton(object.getNames()), 
"names");
+        assertEquals(identifiers,          object.getIdentifiers(), 
"identifiers");
+        assertEquals(gmlID,                object.getID(), "ID");
+        assertRemarksEquals("Adopted by IUGG 1979 Canberra", object, 
Locale.ENGLISH);
+        assertRemarksEquals("Adopté par IUGG 1979 Canberra", object, 
Locale.FRENCH);
         final Code code = object.getIdentifier();
         return (code != null) ? code.getIdentifier() : null;
     }
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/AbstractReferenceSystemTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/AbstractReferenceSystemTest.java
index 6f13ffc51d..e8cea7e541 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/AbstractReferenceSystemTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/AbstractReferenceSystemTest.java
@@ -36,6 +36,7 @@ import org.apache.sis.test.mock.VerticalCRSMock;
 import static org.apache.sis.test.TestUtilities.getSingleton;
 import static org.apache.sis.test.Assertions.assertSerializedEquals;
 import static org.apache.sis.referencing.Assertions.assertWktEquals;
+import static org.apache.sis.referencing.Assertions.assertRemarksEquals;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
 import static org.opengis.referencing.IdentifiedObject.*;
@@ -74,8 +75,8 @@ public final class AbstractReferenceSystemTest extends 
TestCase {
         assertEquals("This is a name",         reference.getName().getCode());
         assertEquals("This is a scope",        scope.toString(Locale.ROOT));
         assertEquals("Valide dans ce domaine", scope.toString(Locale.FRENCH));
-        assertEquals("There is remarks",       
reference.getRemarks().toString(Locale.ENGLISH));
-        assertEquals("Voici des remarques",    
reference.getRemarks().toString(Locale.FRENCH));
+        assertRemarksEquals("There is remarks",    reference, Locale.ENGLISH);
+        assertRemarksEquals("Voici des remarques", reference, Locale.FRENCH);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
index 0a38012253..ad2f72362e 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.referencing;
 
+import java.util.Locale;
 import java.util.Collection;
 import java.awt.geom.Rectangle2D;
 import java.awt.geom.RectangularShape;
@@ -24,6 +25,7 @@ import static java.lang.StrictMath.*;
 import javax.measure.Unit;
 import org.opengis.geometry.Envelope;
 import org.opengis.geometry.DirectPosition;
+import org.opengis.util.InternationalString;
 import org.opengis.metadata.Identifier;
 import org.opengis.parameter.GeneralParameterValue;
 import org.opengis.parameter.ParameterDescriptor;
@@ -140,6 +142,20 @@ public final class Assertions extends Static {
         }
     }
 
+    /**
+     * Asserts that the remarks of the given object are equal to the expected 
value.
+     *
+     * @param expected  the expected remarks, or {@code null}.
+     * @param object    the object for which to test the remarks.
+     * @param locale    the locale to test, or {@code null}.
+     */
+    public static void assertRemarksEquals(final String expected, final 
IdentifiedObject object, final Locale locale) {
+        var remarks = object.getRemarks()
+                .map((locale != null) ? (i18n) -> i18n.toString(locale) : 
InternationalString::toString)
+                .orElse(null);
+        assertEquals(expected, remarks, "remarks");
+    }
+
     /**
      * Compares the given coordinate system axis against the expected values.
      *
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultCartesianCSTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultCartesianCSTest.java
index 174ccda58b..4df261239f 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultCartesianCSTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultCartesianCSTest.java
@@ -34,6 +34,7 @@ import static org.apache.sis.test.TestUtilities.getSingleton;
 import static org.apache.sis.test.Assertions.assertMessageContains;
 import static org.apache.sis.test.Assertions.assertEqualsIgnoreMetadata;
 import static org.apache.sis.referencing.Assertions.assertAxisEquals;
+import static org.apache.sis.referencing.Assertions.assertRemarksEquals;
 import static org.apache.sis.referencing.Assertions.assertEpsgIdentifierEquals;
 
 
@@ -217,7 +218,7 @@ public final class DefaultCartesianCSTest extends TestCase {
         final CoordinateSystemAxis E = cs.getAxis(0);
         final CoordinateSystemAxis N = cs.getAxis(1);
         assertEquals("Easting, northing (E,N)", cs.getName().getCode());
-        assertEquals("Used in ProjectedCRS.", cs.getRemarks().toString());
+        assertRemarksEquals("Used in ProjectedCRS.", cs, null);
         assertEpsgIdentifierEquals("4400", getSingleton(cs.getIdentifiers()));
         assertEpsgIdentifierEquals("1",    getSingleton(E.getIdentifiers()));
         assertEpsgIdentifierEquals("2",    getSingleton(N.getIdentifiers()));
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultEllipsoidalCSTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultEllipsoidalCSTest.java
index 51399872da..2e33bca552 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultEllipsoidalCSTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultEllipsoidalCSTest.java
@@ -30,6 +30,7 @@ import static org.junit.jupiter.api.Assertions.*;
 import org.opengis.test.Validators;
 import org.apache.sis.xml.test.TestCase;
 import static org.apache.sis.referencing.Assertions.assertAxisEquals;
+import static org.apache.sis.referencing.Assertions.assertRemarksEquals;
 import static org.apache.sis.referencing.Assertions.assertEpsgIdentifierEquals;
 import static org.apache.sis.test.TestUtilities.getSingleton;
 
@@ -138,7 +139,7 @@ public final class DefaultEllipsoidalCSTest extends 
TestCase {
         final CoordinateSystemAxis φ = cs.getAxis(0);
         final CoordinateSystemAxis λ = cs.getAxis(1);
         assertEquals("Latitude (north), Longitude (east)",     
cs.getName().getCode());
-        assertEquals("Used in two-dimensional GeographicCRS.", 
cs.getRemarks().toString());
+        assertRemarksEquals("Used in two-dimensional GeographicCRS.", cs, 
null);
         assertAxisEquals("Geodetic latitude",  "φ", AxisDirection.NORTH, -90,  
+90, Units.DEGREE, RangeMeaning.EXACT, φ);
         assertAxisEquals("Geodetic longitude", "λ", AxisDirection.EAST, -180, 
+180, Units.DEGREE, RangeMeaning.WRAPAROUND, λ);
         assertEpsgIdentifierEquals("6422", getSingleton(cs.getIdentifiers()));
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultEllipsoidTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultEllipsoidTest.java
index 9a32294c77..e702cc400f 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultEllipsoidTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultEllipsoidTest.java
@@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.xml.test.TestCase;
 import static org.apache.sis.referencing.Assertions.assertWktEquals;
+import static org.apache.sis.referencing.Assertions.assertRemarksEquals;
 
 
 /**
@@ -129,7 +130,7 @@ public final class DefaultEllipsoidTest extends TestCase {
     public void testEllipsoidXML() throws JAXBException {
         final DefaultEllipsoid ellipsoid = 
unmarshalFile(DefaultEllipsoid.class, openTestFile(false));
         assertEquals("Clarke 1880 (international foot)", 
ellipsoid.getName().getCode());
-        assertEquals("Definition in feet assumed to be international foot.", 
ellipsoid.getRemarks().toString());
+        assertRemarksEquals("Definition in feet assumed to be international 
foot.", ellipsoid, null);
         assertFalse (                    ellipsoid.isSphere());
         assertFalse (                    ellipsoid.isIvfDefinitive());
         assertEquals(20926202,           ellipsoid.getSemiMajorAxis());
@@ -153,7 +154,7 @@ public final class DefaultEllipsoidTest extends TestCase {
     public void testSphereXML() throws JAXBException {
         final DefaultEllipsoid ellipsoid = 
unmarshalFile(DefaultEllipsoid.class, openTestFile(true));
         assertEquals("GRS 1980 Authalic Sphere", 
ellipsoid.getName().getCode());
-        assertEquals("Authalic sphere derived from GRS 1980 ellipsoid (code 
7019).", ellipsoid.getRemarks().toString());
+        assertRemarksEquals("Authalic sphere derived from GRS 1980 ellipsoid 
(code 7019).", ellipsoid, null);
         assertTrue  (                          ellipsoid.isSphere());
         assertFalse (                          ellipsoid.isIvfDefinitive());
         assertEquals(6371007,                  ellipsoid.getSemiMajorAxis());
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.java
index b693b6c742..c89b830d0f 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.java
@@ -45,6 +45,7 @@ import static org.apache.sis.test.TestUtilities.getSingleton;
 import static org.apache.sis.test.Assertions.assertSerializedEquals;
 import static org.apache.sis.metadata.Assertions.assertXmlEquals;
 import static org.apache.sis.referencing.Assertions.assertWktEquals;
+import static org.apache.sis.referencing.Assertions.assertRemarksEquals;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
 import static org.opengis.test.Assertions.assertMatrixEquals;
@@ -108,9 +109,9 @@ public final class DefaultGeodeticDatumTest extends 
TestCase {
         assertEquals("This is a name",        datum.getName().getCode());
         assertEquals("This is a scope",       scope.toString(Locale.ROOT));
         assertEquals("Valide pour tel usage", scope.toString(Locale.FRENCH));
-        assertEquals("There is remarks",      
datum.getRemarks().toString(Locale.ROOT));
-        assertEquals("Voici des remarques",   
datum.getRemarks().toString(Locale.FRENCH));
-        assertEquals("注です。",                
datum.getRemarks().toString(Locale.JAPANESE));
+        assertRemarksEquals("There is remarks",      datum, Locale.ROOT);
+        assertRemarksEquals("Voici des remarques",   datum, Locale.FRENCH);
+        assertRemarksEquals("注です。",              datum, Locale.JAPANESE);
     }
 
     /**
@@ -301,16 +302,12 @@ public final class DefaultGeodeticDatumTest extends 
TestCase {
          * Values in the following tests are specific to our XML file.
          * The actual texts in the EPSG database are more descriptive.
          */
-        assertEquals("No distinction between the original and subsequent WGS 
84 frames.",
-                datum.getRemarks().toString());
-        assertEquals("Satellite navigation.",
-                getScope(datum));
+        assertRemarksEquals("No distinction between the original and 
subsequent WGS 84 frames.", datum, null);
+        assertEquals("Satellite navigation.", getScope(datum));
         assertEquals("Station coordinates changed by a few centimetres in 
1994, 1997, 2002 and 2012.",
-                datum.getAnchorDefinition().get().toString());
-        assertEquals(xmlDate("1984-01-01 00:00:00").toInstant(),
-                datum.getAnchorEpoch().orElse(null));
-        assertEquals("Defining parameters cited in EPSG database.",
-                datum.getEllipsoid().getRemarks().toString());
+                     datum.getAnchorDefinition().orElseThrow().toString());
+        assertEquals(xmlDate("1984-01-01 00:00:00").toInstant(), 
datum.getAnchorEpoch().orElseThrow());
+        assertRemarksEquals("Defining parameters cited in EPSG database.", 
datum.getEllipsoid(), null);
         return datum;
     }
 
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultPrimeMeridianTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultPrimeMeridianTest.java
index c7c77e6199..d415906e71 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultPrimeMeridianTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultPrimeMeridianTest.java
@@ -37,6 +37,7 @@ import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.xml.test.TestCase;
 import static org.apache.sis.metadata.Assertions.assertXmlEquals;
 import static org.apache.sis.referencing.Assertions.assertWktEquals;
+import static org.apache.sis.referencing.Assertions.assertRemarksEquals;
 
 
 /**
@@ -183,7 +184,7 @@ public final class DefaultPrimeMeridianTest extends 
TestCase {
         final DefaultPrimeMeridian pm = 
unmarshalFile(DefaultPrimeMeridian.class, openTestFile(false));
         assertIsParis(pm);
         assertEquals(2.33722917, pm.getGreenwichLongitude(Units.DEGREE), 
1E-12);
-        assertEquals("Equivalent to 2°20′14.025″.", 
pm.getRemarks().toString());
+        assertRemarksEquals("Equivalent to 2°20′14.025″.", pm, null);
         assertNull(pm.getName().getCodeSpace());
         assertWktEquals(Convention.WKT1,
                 "PRIMEM[“Paris”, 2.33722917, AUTHORITY[“EPSG”, “8903”]]", pm);
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultTemporalDatumTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultTemporalDatumTest.java
index 887c1f8870..80821b864b 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultTemporalDatumTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultTemporalDatumTest.java
@@ -30,6 +30,7 @@ import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.xml.test.TestCase;
 import org.apache.sis.metadata.iso.citation.HardCodedCitations;
 import static org.apache.sis.referencing.Assertions.assertWktEquals;
+import static org.apache.sis.referencing.Assertions.assertRemarksEquals;
 import static org.apache.sis.test.TestUtilities.getSingleton;
 import static org.apache.sis.test.TestUtilities.getScope;
 
@@ -126,7 +127,7 @@ public final class DefaultTemporalDatumTest extends 
TestCase {
         assertIdentifierEquals("Apache Spatial Information System", "SIS", 
null, "MJ",
                                getSingleton(datum.getIdentifiers()), 
"identifier");
         assertEquals("Modified Julian", datum.getName().getCode());
-        assertEquals("Time measured as days since November 17, 1858 at 00:00 
UTC.", datum.getRemarks().toString());
+        assertRemarksEquals("Time measured as days since November 17, 1858 at 
00:00 UTC.", datum, null);
         assertEquals("History.", getScope(datum));
         assertEquals(new Date(ORIGIN), datum.getOrigin());
     }
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultVerticalDatumTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultVerticalDatumTest.java
index 50234ca791..5a2241fb77 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultVerticalDatumTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultVerticalDatumTest.java
@@ -36,6 +36,7 @@ import org.apache.sis.xml.test.TestCase;
 import static org.apache.sis.test.TestUtilities.getScope;
 import static org.apache.sis.metadata.Assertions.assertXmlEquals;
 import static org.apache.sis.referencing.Assertions.assertWktEquals;
+import static org.apache.sis.referencing.Assertions.assertRemarksEquals;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
 import org.opengis.referencing.datum.RealizationMethod;
@@ -98,7 +99,7 @@ public final class DefaultVerticalDatumTest extends TestCase {
          * Values in the following tests are specific to our XML file.
          * The actual texts in the EPSG database are more descriptive.
          */
-        assertEquals("Approximates geoid.",             
datum.getRemarks().toString());
+        assertRemarksEquals("Approximates geoid.",      datum, null);
         assertEquals("Hydrography.",                    getScope(datum));
         assertEquals("Averaged over a 19-year period.", 
datum.getAnchorDefinition().get().toString());
         /*
@@ -131,8 +132,8 @@ public final class DefaultVerticalDatumTest extends 
TestCase {
          * those property does not have the same XML element name (SIS-160).
          * Below is all we have.
          */
-        assertEquals("Approximates geoid.", datum.getRemarks().toString());
-        assertEquals("Hydrography.",        getScope(datum));
+        assertRemarksEquals("Approximates geoid.", datum, null);
+        assertEquals("Hydrography.", getScope(datum));
         /*
          * Test marshalling. We cannot yet compare with the original XML file
          * because of all the information lost. This may be fixed in a future
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/SingleOperationMarshallingTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/SingleOperationMarshallingTest.java
index 9ced366669..ebd448c3e9 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/SingleOperationMarshallingTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/SingleOperationMarshallingTest.java
@@ -156,7 +156,7 @@ public final class SingleOperationMarshallingTest extends 
TestCase.WithLogs {
         assertEquals("World Mercator", c.getName().getCode(), "name");
         assertEquals("3395", getSingleton(c.getIdentifiers()).getCode(), 
"identifier");
         assertEquals("Very small scale mapping.", getScope(c), "scope");
-        assertNull  (c.getOperationVersion(), "operationVersion");
+        assertTrue  (c.getOperationVersion().isEmpty(), "operationVersion");
 
         final GeographicBoundingBox e = getDomainOfValidity(c);
         assertEquals(+180, e.getEastBoundLongitude(), "eastBoundLongitude");
@@ -221,7 +221,7 @@ public final class SingleOperationMarshallingTest extends 
TestCase.WithLogs {
         assertEquals("NTF (Paris) to NTF (1)", c.getName().getCode(), "name");
         assertEquals("1763", getSingleton(c.getIdentifiers()).getCode(), 
"identifier");
         assertEquals("Change of prime meridian.", getScope(c), "scope");
-        assertEquals("IGN-Fra", c.getOperationVersion(), "operationVersion");
+        assertEquals("IGN-Fra", c.getOperationVersion().get(), 
"operationVersion");
 
         final OperationMethod method = c.getMethod();
         assertNotNull(method, "method");
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java
index 54746ba1ed..b4a1c3e11b 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java
@@ -231,8 +231,8 @@ public final class ProvidersTest extends TestCase {
      */
     @Test
     public void testDescription() {
-        assertNotEquals(0, 
SatelliteTracking.SATELLITE_ORBIT_INCLINATION.getDescription().length());
-        assertNotEquals(0, SatelliteTracking.SATELLITE_ORBITAL_PERIOD   
.getDescription().length());
-        assertNotEquals(0, SatelliteTracking.ASCENDING_NODE_PERIOD      
.getDescription().length());
+        assertNotEquals(0, 
SatelliteTracking.SATELLITE_ORBIT_INCLINATION.getDescription().orElseThrow().length());
+        assertNotEquals(0, SatelliteTracking.SATELLITE_ORBITAL_PERIOD   
.getDescription().orElseThrow().length());
+        assertNotEquals(0, SatelliteTracking.ASCENDING_NODE_PERIOD      
.getDescription().orElseThrow().length());
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java
index 1327cf31b4..197a69ff66 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java
@@ -332,7 +332,7 @@ public class CoordinateOperationMethods extends 
HTMLGenerator {
             reopenTag("tr");
             println("td", escape(getFirstEpsgCode(param.getIdentifiers())));
             writeName(param);
-            String remarks = toLocalizedString(param.getRemarks());
+            String remarks = 
toLocalizedString(param.getRemarks().orElse(null));
             if (remarks != null) {
                 Integer index = footnotes.putIfAbsent(remarks, 
footnotes.size() + 1);
                 if (index == null) {
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateReferenceSystems.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateReferenceSystems.java
index feab090c11..219b9ef155 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateReferenceSystems.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateReferenceSystems.java
@@ -538,10 +538,10 @@ public final class CoordinateReferenceSystems extends 
AuthorityCodesReport {
             row.isDeprecated = dep.isDeprecated();
             if (row.isDeprecated) {
                 String replacedBy = null;
-                InternationalString i18n = object.getRemarks();
+                InternationalString i18n = object.getRemarks().orElse(null);
                 for (final Identifier id : object.getIdentifiers()) {
                     if (id instanceof Deprecable did && did.isDeprecated()) {
-                        i18n = did.getRemarks();
+                        i18n = did.getRemarks().orElse(null);
                         if (id instanceof DeprecatedCode dc) {
                             replacedBy = dc.replacedBy;
                         }
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/xml/bind/referencing/CC_GeneralOperationParameterTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/xml/bind/referencing/CC_GeneralOperationParameterTest.java
index 8b04e2732c..9e45adb3ce 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/xml/bind/referencing/CC_GeneralOperationParameterTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/xml/bind/referencing/CC_GeneralOperationParameterTest.java
@@ -31,6 +31,7 @@ import org.apache.sis.xml.Namespaces;
 // Test dependencies
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
+import static org.apache.sis.referencing.Assertions.assertRemarksEquals;
 import org.apache.sis.xml.test.TestCase;
 
 
@@ -71,8 +72,8 @@ public final class CC_GeneralOperationParameterTest extends 
TestCase.WithLogs {
          * illegal value) for this test.
          */
         assertEquals(name, p.getName().getCode());
-        assertEquals(remarks, (remarks == null) ? null : 
p.getRemarks().toString());
-        assertNull(p.getDescription());
+        assertEquals(remarks, (remarks == null) ? null : 
p.getRemarks().orElseThrow().toString());
+        assertTrue(p.getDescription().isEmpty());
         assertNull(p.getValueClass());
         assertEquals(0, p.getMinimumOccurs());
         assertEquals(1, p.getMaximumOccurs());
@@ -142,7 +143,7 @@ public final class CC_GeneralOperationParameterTest extends 
TestCase.WithLogs {
         assertEquals (0,                  merged.getMinimumOccurs());  // From 
provided descriptor.
         assertEquals (1,                  merged.getMaximumOccurs());
         assertEquals (Integer.class,      merged.getValueClass());     // From 
complete descriptor.
-        assertNull   (                    merged.getRemarks());
+        assertTrue   (                    merged.getRemarks().isEmpty());
 
         complete = create("Test parameter", null, false, null);
         assertSame(complete, CC_GeneralOperationParameter.merge(provided, 
complete));
@@ -155,7 +156,7 @@ public final class CC_GeneralOperationParameterTest extends 
TestCase.WithLogs {
         assertEquals (0,                     merged.getMinimumOccurs());
         assertEquals (1,                     merged.getMaximumOccurs());
         assertEquals (Integer.class,         merged.getValueClass());
-        assertSame   (provided.getRemarks(), merged.getRemarks());
+        assertEquals (provided.getRemarks(), merged.getRemarks());
         loggings.assertNoUnexpectedLog();
     }
 
@@ -169,13 +170,13 @@ public final class CC_GeneralOperationParameterTest 
extends TestCase.WithLogs {
     public void testGroupSubstitution() throws JAXBException {
         final Map<String,String> properties = new HashMap<>(4);
         assertNull(properties.put(DefaultParameterDescriptor.NAME_KEY, 
"Group"));
-        final ParameterDescriptorGroup provided = new 
DefaultParameterDescriptorGroup(properties, 1, 2,
+        final var provided = new DefaultParameterDescriptorGroup(properties, 
1, 2,
                 unmarshal("Parameter A", null),
                 unmarshal("Parameter B", "Remarks B."),
                 unmarshal("Parameter C", null));
 
         assertNull(properties.put(DefaultParameterDescriptor.REMARKS_KEY, 
"More details here."));
-        final ParameterDescriptorGroup complete = new 
DefaultParameterDescriptorGroup(properties, 1, 2,
+        final var complete = new DefaultParameterDescriptorGroup(properties, 
1, 2,
                 create("Parameter A", "Remarks A.", false, 3),
                 create("Parameter B", "Remarks B.", false, 4),
                 create("Parameter C", "Remarks C.", false, 5),
@@ -196,23 +197,22 @@ public final class CC_GeneralOperationParameterTest 
extends TestCase.WithLogs {
     public void testGroupMergeBecauseDifferentProperties() throws 
JAXBException {
         final Map<String,String> properties = new HashMap<>(4);
         assertNull(properties.put(DefaultParameterDescriptor.NAME_KEY, 
"Group"));
-        final ParameterDescriptorGroup provided = new 
DefaultParameterDescriptorGroup(properties, 1, 2,
+        final var provided = new DefaultParameterDescriptorGroup(properties, 
1, 2,
                 unmarshal("Parameter A", "Remarks A."),
                 unmarshal("Parameter B", "Remarks B."),
                 unmarshal("Parameter C", "Remarks C."));
 
         assertNull(properties.put(DefaultParameterDescriptor.REMARKS_KEY, 
"More details here."));
-        final ParameterDescriptorGroup complete = new 
DefaultParameterDescriptorGroup(properties, 1, 2,
+        final var complete = new DefaultParameterDescriptorGroup(properties, 
1, 2,
                 create("Parameter A", "Remarks A.", true,  3),
                 create("Parameter B", "Remarks B.", false, 4),
                 create("Parameter C", "Different.", false, 5),
                 create("Parameter D", "Remarks D.", false, 6));
 
-        final ParameterDescriptorGroup merged =
-                (ParameterDescriptorGroup) 
CC_GeneralOperationParameter.merge(provided, complete);
+        final var merged = (ParameterDescriptorGroup) 
CC_GeneralOperationParameter.merge(provided, complete);
         assertNotSame(complete, provided);
         assertSame   (complete.getName(),    merged.getName());
-        assertSame   (complete.getRemarks(), merged.getRemarks());
+        assertEquals (complete.getRemarks(), merged.getRemarks());
         assertEquals (1,                     merged.getMinimumOccurs());
         assertEquals (2,                     merged.getMaximumOccurs());
 
@@ -237,21 +237,20 @@ public final class CC_GeneralOperationParameterTest 
extends TestCase.WithLogs {
     public void testGroupMergeBecauseMissingParameter() throws JAXBException {
         final Map<String,String> properties = new HashMap<>(4);
         assertNull(properties.put(DefaultParameterDescriptor.NAME_KEY, 
"Group"));
-        final ParameterDescriptorGroup provided = new 
DefaultParameterDescriptorGroup(properties, 1, 2,
+        final var provided = new DefaultParameterDescriptorGroup(properties, 
1, 2,
                 unmarshal("Parameter A", null),
                 unmarshal("Parameter C", null));
 
         assertNull(properties.put(DefaultParameterDescriptor.REMARKS_KEY, 
"More details here."));
-        final ParameterDescriptorGroup complete = new 
DefaultParameterDescriptorGroup(properties, 1, 2,
+        final var complete = new DefaultParameterDescriptorGroup(properties, 
1, 2,
                 create("Parameter A", null, false, 3),
                 create("Parameter B", null, true,  4),
                 create("Parameter C", null, false, 5));
 
-        final ParameterDescriptorGroup merged =
-                (ParameterDescriptorGroup) 
CC_GeneralOperationParameter.merge(provided, complete);
+        final var merged = (ParameterDescriptorGroup) 
CC_GeneralOperationParameter.merge(provided, complete);
         assertNotSame(complete, provided);
         assertSame   (complete.getName(),    merged.getName());
-        assertSame   (complete.getRemarks(), merged.getRemarks());
+        assertEquals (complete.getRemarks(), merged.getRemarks());
         assertEquals (1,                     merged.getMinimumOccurs());
         assertEquals (2,                     merged.getMaximumOccurs());
 
@@ -279,22 +278,21 @@ public final class CC_GeneralOperationParameterTest 
extends TestCase.WithLogs {
     public void testGroupMergeBecauseExtraParameter() throws JAXBException {
         final Map<String,String> properties = new HashMap<>(4);
         assertNull(properties.put(DefaultParameterDescriptor.NAME_KEY, 
"Group"));
-        final ParameterDescriptorGroup provided = new 
DefaultParameterDescriptorGroup(properties, 1, 2,
+        final var provided = new DefaultParameterDescriptorGroup(properties, 
1, 2,
                 unmarshal("Parameter A", "Remarks A."),
                 unmarshal("Parameter B", "Remarks B."),
                 unmarshal("Parameter C", "Remarks C."));
 
         assertNull(properties.put(DefaultParameterDescriptor.REMARKS_KEY, 
"More details here."));
-        final ParameterDescriptorGroup complete = new 
DefaultParameterDescriptorGroup(properties, 1, 2,
+        final var complete = new DefaultParameterDescriptorGroup(properties, 
1, 2,
                 create("Parameter A", "Remarks A.", false, 3),
                 create("Parameter C", "Remarks C.", false, 4));
 
         loggings.assertNoUnexpectedLog();
-        final ParameterDescriptorGroup merged =
-                (ParameterDescriptorGroup) 
CC_GeneralOperationParameter.merge(provided, complete);
+        final var merged = (ParameterDescriptorGroup) 
CC_GeneralOperationParameter.merge(provided, complete);
         assertNotSame(complete, provided);
         assertSame   (complete.getName(),    merged.getName());
-        assertSame   (complete.getRemarks(), merged.getRemarks());
+        assertEquals (complete.getRemarks(), merged.getRemarks());
         assertEquals (1,                     merged.getMinimumOccurs());
         assertEquals (2,                     merged.getMaximumOccurs());
         loggings.assertNextLogContains("Parameter B", "Group");
@@ -306,7 +304,7 @@ public final class CC_GeneralOperationParameterTest extends 
TestCase.WithLogs {
 
         final GeneralParameterDescriptor extra = itm.next();
         assertEquals("Parameter B", extra.getName().getCode());
-        assertEquals("Remarks B.",  extra.getRemarks().toString());
+        assertRemarksEquals("Remarks B.", extra, null);
 
         verifyParameter(itc.next(), itm.next(), true, "Remarks C.");
         assertFalse(itc.hasNext());
@@ -326,6 +324,6 @@ public final class CC_GeneralOperationParameterTest extends 
TestCase.WithLogs {
         assertEquals(0,                  merged.getMinimumOccurs());
         assertEquals(1,                  merged.getMaximumOccurs());
         assertEquals(Integer.class,      ((ParameterDescriptor<?>) 
merged).getValueClass());
-        assertEquals(remarks,            (remarks == null) ? null : 
merged.getRemarks().toString());
+        assertRemarksEquals(remarks,     merged, null);
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/xml/bind/referencing/CC_OperationParameterGroupTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/xml/bind/referencing/CC_OperationParameterGroupTest.java
index b03153c84b..7dc1aec31b 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/xml/bind/referencing/CC_OperationParameterGroupTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/xml/bind/referencing/CC_OperationParameterGroupTest.java
@@ -155,7 +155,7 @@ public final class CC_OperationParameterGroupTest extends 
TestCase {
         assertNotSame(incomplete, complete, "Latitude of natural origin");
         assertNotSame(fromValue,  complete, "Latitude of natural origin");
         assertSame   (fromValue .getName(),       complete.getName());
-        assertSame   (incomplete.getRemarks(),    complete.getRemarks());
+        assertEquals (incomplete.getRemarks(),    complete.getRemarks());
         assertEquals (Double.class,               complete.getValueClass());
         assertSame   (fromValue.getValueDomain(), complete.getValueDomain());
         /*
@@ -219,9 +219,9 @@ public final class CC_OperationParameterGroupTest extends 
TestCase {
         assertSame(expected.getMaximumValue(), actual.getMaximumValue());
         assertSame(expected.getDefaultValue(), actual.getDefaultValue());
         if (remarks != null) {
-            assertEquals(remarks, actual.getRemarks().toString());
+            assertEquals(remarks, actual.getRemarks().get().toString());
         } else {
-            assertSame(expected.getRemarks(), actual.getRemarks());
+            assertEquals(expected.getRemarks(), actual.getRemarks());
         }
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/TypesTest.java
 
b/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/TypesTest.java
index 5408ff3b02..589e94d7ee 100644
--- 
a/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/TypesTest.java
+++ 
b/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/TypesTest.java
@@ -67,7 +67,7 @@ public final class TypesTest extends TestCase {
             final GenericName name = p.getName();
             if (!AttributeConvention.contains(name)) {
                 final String label = name.toString();
-                assertNonEmpty(label, p.getDesignation());
+                assertNonEmpty(label, p.getDesignation().orElse(null));
                 assertNonEmpty(label, p.getDefinition());
             }
         }
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/folder/StoreProvider.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/folder/StoreProvider.java
index 24412acf85..dd72fe7799 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/folder/StoreProvider.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/folder/StoreProvider.java
@@ -117,7 +117,7 @@ public final class StoreProvider extends DataStoreProvider {
      * Creates a parameter descriptor equals to the given one except for the 
remarks which are set to the given value.
      */
     private static <T> ParameterDescriptor<T> annotate(ParameterBuilder 
builder, ParameterDescriptor<T> e, InternationalString remark) {
-        return 
builder.addName(e.getName()).setDescription(e.getDescription()).setRemarks(remark).create(e.getValueClass(),
 null);
+        return 
builder.addName(e.getName()).setDescription(e.getDescription().orElse(null)).setRemarks(remark).create(e.getValueClass(),
 null);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Classes.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Classes.java
index 1c86c7aa08..c14ee6774a 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Classes.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Classes.java
@@ -23,6 +23,7 @@ import java.util.Iterator;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashSet;
+import java.util.Optional;
 import java.lang.reflect.Type;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
@@ -170,6 +171,8 @@ public final class Classes extends Static {
      * @param  field  the field for which to obtain the parameterized type.
      * @return the upper bound of parameterized type, or {@code null} if the 
given field
      *         is not of a parameterized type.
+     *
+     * @see #isParameterizedProperty(Class)
      */
     public static Class<?> boundOfParameterizedProperty(final Field field) {
         return getActualTypeArgument(field.getGenericType());
@@ -190,6 +193,8 @@ public final class Classes extends Static {
      * @param  method  the getter or setter method for which to obtain the 
parameterized type.
      * @return the upper bound of parameterized type, or {@code null} if the 
given method
      *         is not a getter or setter for a property of a parameterized 
type.
+     *
+     * @see #isParameterizedProperty(Class)
      */
     public static Class<?> boundOfParameterizedProperty(final Method method) {
         final Type[] parameters = method.getGenericParameterTypes();
@@ -858,6 +863,31 @@ cmp:    for (final Class<?> c : c1) {
               !ArraysExt.contains(EXCLUDES, method.getName());
     }
 
+    /**
+     * Returns whether the actual type of a property is the parameterized type 
according SIS.
+     * The given {@code type} argument should be the type of a field or the 
return type of a
+     * {@linkplain #isPossibleGetter(Method) getter method}. If this method 
returns {@code true},
+     * then a {@code boundOfParameterizedProperty(…)} method needs to be 
invoked in order to get
+     * the actual property type.
+     *
+     * <p>The current implementation tests only if the given type is {@link 
Optional}.
+     * More types may be added in future Apache SIS versions, depending on API 
evolutions.
+     * Note that collections are intentionally <em>not</em> recognized by this 
method,
+     * because they usually need to be handled in a special way by the 
caller.</p>
+     *
+     * @param  type  the field type or getter method return type to test.
+     * @return whether a {@code boundOfParameterizedProperty(…)} method need 
to be invoked
+     *         for getting the actual property type.
+     *
+     * @see #boundOfParameterizedProperty(Field)
+     * @see #boundOfParameterizedProperty(Method)
+     *
+     * @since 1.5
+     */
+    public static boolean isParameterizedProperty(final Class<?> type) {
+        return type == Optional.class;
+    }
+
     /**
      * Returns {@code true} if the given class is non-null, public and 
exported.
      *
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Deprecable.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Deprecable.java
index 197f995be2..249ce360a6 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Deprecable.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Deprecable.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.util;
 
+import java.util.Optional;
 import org.opengis.util.InternationalString;
 
 
@@ -41,7 +42,7 @@ import org.opengis.util.InternationalString;
  * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.3
+ * @version 1.5
  * @since   0.3
  */
 public interface Deprecable {
@@ -59,8 +60,10 @@ public interface Deprecable {
      *
      * <div class="note"><b>Example:</b> "superseded by code XYZ".</div>
      *
-     * @return comments about this instance, or {@code null} if none. Shall be 
the reason for deprecation
+     * @return comments about this instance, or empty if none. Shall be the 
reason for deprecation
      *         or the alternative to use if this instance {@linkplain 
#isDeprecated() is deprecated}.
      */
-    InternationalString getRemarks();
+    default Optional<InternationalString> getRemarks() {
+        return Optional.empty();
+    }
 }
diff --git a/geoapi/snapshot b/geoapi/snapshot
index 1685784018..3f1075267a 160000
--- a/geoapi/snapshot
+++ b/geoapi/snapshot
@@ -1 +1 @@
-Subproject commit 1685784018d4e32484a2eedb6e63a41751335101
+Subproject commit 3f1075267a6ad5495b8218521b7efbd86f1f177c
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/dataset/FeatureTable.java
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/dataset/FeatureTable.java
index a429e4ca1a..29314d556f 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/dataset/FeatureTable.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/dataset/FeatureTable.java
@@ -282,7 +282,7 @@ public class FeatureTable extends TableView<Feature> {
              */
             final GenericName qualifiedName = pt.getName();
             final String name = qualifiedName.toString();
-            String title = string(pt.getDesignation());
+            String title = string(pt.getDesignation().orElse(null));
             if (title == null) {
                 title = string(qualifiedName.toInternationalString());
                 if (title == null) title = name;

Reply via email to