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

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

commit b1954e2e7f150d5ba37b8548bdd5cd0ba7a996dc
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Wed Jul 16 15:07:21 2025 +0200

    Add `getCharacteristicValue(…)` for making possible to ask for a 
characteristic
    without forcing the creation of a `Property` instance in the common case 
where
    the characteristic does not exist.
    
    Contains some opportunistic code reformatting.
---
 .../org/apache/sis/feature/AbstractFeature.java    | 55 +++++++++++++++++++--
 .../main/org/apache/sis/feature/DenseFeature.java  | 35 ++++++-------
 .../main/org/apache/sis/feature/Features.java      | 14 +++---
 .../main/org/apache/sis/feature/SparseFeature.java | 35 ++++++-------
 .../sis/feature/privy/AttributeConvention.java     | 57 +++++++++++++---------
 .../main/org/apache/sis/filter/internal/Node.java  | 10 ++--
 .../feature/builder/AttributeTypeBuilderTest.java  |  2 +-
 .../sis/feature/privy/AttributeConventionTest.java |  9 ++--
 .../sis/storage/netcdf/base/FeatureSetTest.java    |  6 +--
 9 files changed, 143 insertions(+), 80 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractFeature.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractFeature.java
index 01b4d7ca64..1ac43343df 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractFeature.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractFeature.java
@@ -17,6 +17,7 @@
 package org.apache.sis.feature;
 
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Iterator;
 import java.util.Collection;
 import java.util.Collections;
@@ -86,7 +87,7 @@ import org.opengis.feature.Operation;
  * @author  Travis L. Pinney
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.5
  *
  * @see DefaultFeatureType#newInstance()
  *
@@ -103,7 +104,7 @@ public abstract class AbstractFeature implements Feature, 
Serializable {
      *
      * @see #getValueOrFallback(String, Object)
      */
-    static final Object MISSING = new Object();
+    private static final Object MISSING = new Object();
 
     /**
      * Information about the feature (name, characteristics, <i>etc.</i>).
@@ -349,7 +350,11 @@ public abstract class AbstractFeature implements Feature, 
Serializable {
      * @see AbstractAttribute#getValue()
      */
     @Override
-    public abstract Object getPropertyValue(final String name) throws 
PropertyNotFoundException;
+    public Object getPropertyValue(final String name) throws 
PropertyNotFoundException {
+        final Object value = getValueOrFallback(name, MISSING);
+        if (value != MISSING) return value;
+        throw new PropertyNotFoundException(propertyNotFound(type, getName(), 
name));
+    }
 
     /**
      * Sets the value for the property of the given name.
@@ -470,6 +475,50 @@ public abstract class AbstractFeature implements Feature, 
Serializable {
         }
     }
 
+    /**
+     * Returns the explicit or default value of a characteristic of a property.
+     * This is a shortcut for the following chain of method invocations
+     * (cast and null checks omitted for brevity),
+     * except that the actual implementation is potentially more efficient:
+     *
+     * {@snippet lang="java" :
+     * return Optional.ofNullable(
+     *         ((Attribute<?>) getProperty(property))
+     *         .characteristics()
+     *         .get(characteristic)
+     *         .getValue());
+     * }
+     *
+     * If the attribute has no {@linkplain AbstractAttribute#characteristics() 
characteristic} of the given name,
+     * then this method fallbacks on the default value of the {@linkplain 
DefaultAttributeType#characteristics()
+     * characteristics of the attribute type}.
+     *
+     * @param  property        name of the property for which to get a 
characteristic.
+     * @param  characteristic  name of the characteristic of the property of 
the given name.
+     * @return value of the specified characteristic on the specified 
property, or an empty value
+     *         if the property is not an attribute or the attribute has no 
such characteristic.
+     * @throws PropertyNotFoundException if the {@code property} argument is 
not the name of a property of this feature.
+     *
+     * @since 1.5
+     */
+    public Optional<?> getCharacteristicValue(final String property, final 
String characteristic)
+            throws PropertyNotFoundException
+    {
+        Property p = getProperty(property);
+        if (p instanceof Attribute<?>) {
+            var attribute = (Attribute<?>) p;
+            Attribute<?> ca = attribute.characteristics().get(characteristic);
+            if (ca != null) {
+                // If the characteristic is present, assume that an explicitly 
null value is intentional.
+                return Optional.ofNullable(ca.getValue());
+            } else {
+                return 
Optional.ofNullable(attribute.getType().characteristics().get(characteristic))
+                        .map(AttributeType::getDefaultValue);
+            }
+        }
+        return Optional.empty();
+    }
+
     /**
      * Returns the value of the given attribute, as a singleton or as a 
collection depending
      * on the maximum number of occurrences.
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/DenseFeature.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/DenseFeature.java
index 6cdfb9c7e7..db9fd2c693 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/DenseFeature.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/DenseFeature.java
@@ -18,6 +18,7 @@ package org.apache.sis.feature;
 
 import java.util.Map;
 import java.util.Arrays;
+import java.util.Optional;
 import org.opengis.metadata.maintenance.ScopeCode;
 import org.opengis.metadata.quality.DataQuality;
 import org.apache.sis.util.ArgumentChecks;
@@ -171,20 +172,6 @@ final class DenseFeature extends AbstractFeature 
implements CloneAccess {
         properties = c;     // Store only on success.
     }
 
-    /**
-     * Returns the value for the property of the given name.
-     *
-     * @param  name  the property name.
-     * @return the value for the given property, or {@code null} if none.
-     * @throws PropertyNotFoundException if the given argument is not an 
attribute or association name of this feature.
-     */
-    @Override
-    public Object getPropertyValue(final String name) throws 
PropertyNotFoundException {
-        final Object value = getValueOrFallback(name, MISSING);
-        if (value != MISSING) return value;
-        throw new PropertyNotFoundException(propertyNotFound(type, getName(), 
name));
-    }
-
     /**
      * Returns the value for the property of the given name if that property 
exists, or a fallback value otherwise.
      *
@@ -258,6 +245,20 @@ final class DenseFeature extends AbstractFeature 
implements CloneAccess {
         setPropertyValue(property, value);
     }
 
+    /**
+     * Returns the explicit or default value of a characteristic of a property.
+     * Overridden for skipping the creation of property instances when there 
is no characteristic.
+     */
+    @Override
+    public Optional<?> getCharacteristicValue(final String property, final 
String characteristic)
+            throws PropertyNotFoundException
+    {
+        if (properties instanceof Property[]) {
+            return super.getCharacteristicValue(property, characteristic);
+        }
+        return Optional.empty();
+    }
+
     /**
      * Verifies if all current properties met the constraints defined by the 
feature type. This method returns
      * {@linkplain 
org.apache.sis.metadata.iso.quality.DefaultDataQuality#getReports() reports} 
for all invalid
@@ -266,11 +267,11 @@ final class DenseFeature extends AbstractFeature 
implements CloneAccess {
     @Override
     public DataQuality quality() {
         if (properties != null && !(properties instanceof Property[])) {
-            final Validator v = new Validator(ScopeCode.FEATURE);
+            final var validator = new Validator(ScopeCode.FEATURE);
             for (final Map.Entry<String, Integer> entry : indices.entrySet()) {
-                v.validateAny(type.getProperty(entry.getKey()), 
properties[entry.getValue()]);
+                validator.validateAny(type.getProperty(entry.getKey()), 
properties[entry.getValue()]);
             }
-            return v.quality;
+            return validator.quality;
         }
         /*
          * Slower path when there is a possibility that user overridden the 
Property.quality() methods.
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/Features.java 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/Features.java
index 9e7de95a16..6cd9577401 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/Features.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/Features.java
@@ -84,7 +84,8 @@ public final class Features extends Static {
              * the latter case we could have (to be strict) to return a <? 
extends V> type.
              */
             if (!valueClass.equals(actual)) {
-                throw new 
ClassCastException(Resources.format(Resources.Keys.MismatchedValueClass_3,
+                throw new ClassCastException(Resources.format(
+                        Resources.Keys.MismatchedValueClass_3,
                         type.getName(), valueClass, actual));
             }
         }
@@ -115,7 +116,8 @@ public final class Features extends Static {
              * the latter case we could have (to be strict) to return a <? 
extends V> type.
              */
             if (!valueClass.equals(actual)) {
-                throw new 
ClassCastException(Resources.format(Resources.Keys.MismatchedValueClass_3,
+                throw new ClassCastException(Resources.format(
+                        Resources.Keys.MismatchedValueClass_3,
                         attribute.getName(), valueClass, actual));
             }
         }
@@ -183,7 +185,7 @@ public final class Features extends Static {
                  * contain a cycle. However, given that the consequence of an 
infinite cycle here
                  * would be thread freeze, we check as a safety.
                  */
-                final Map<IdentifiedType,Boolean> done = new 
IdentityHashMap<>(4);
+                final var done = new 
IdentityHashMap<IdentifiedType,Boolean>(4);
                 while (!target.isInstance(type = ((Operation) 
type).getResult())) {
                     if (!(type instanceof Operation) || done.put(type, 
Boolean.TRUE) != null) {
                         return Optional.empty();
@@ -352,9 +354,9 @@ public final class Features extends Static {
             if (feature instanceof AbstractFeature) {
                 quality = ((AbstractFeature) feature).quality();
             } else {
-                final Validator v = new Validator(ScopeCode.FEATURE);
-                v.validate(feature.getType(), feature);
-                quality = v.quality;
+                final var validator = new Validator(ScopeCode.FEATURE);
+                validator.validate(feature.getType(), feature);
+                quality = validator.quality;
             }
             /*
              * Loop on quality elements and check conformance results.
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/SparseFeature.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/SparseFeature.java
index 8fdb173a5f..76a110b426 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/SparseFeature.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/SparseFeature.java
@@ -19,6 +19,7 @@ package org.apache.sis.feature;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.ConcurrentModificationException;
 import org.opengis.metadata.maintenance.ScopeCode;
 import org.opengis.metadata.quality.DataQuality;
@@ -218,20 +219,6 @@ final class SparseFeature extends AbstractFeature 
implements CloneAccess {
         properties.put(indices.get(name), property);
     }
 
-    /**
-     * Returns the value for the property of the given name.
-     *
-     * @param  name  the property name.
-     * @return the value for the given property, or {@code null} if none.
-     * @throws PropertyNotFoundException if the given argument is not an 
attribute or association name of this feature.
-     */
-    @Override
-    public Object getPropertyValue(final String name) throws 
PropertyNotFoundException {
-        final Object value = getValueOrFallback(name, MISSING);
-        if (value != MISSING) return value;
-        throw new PropertyNotFoundException(propertyNotFound(type, getName(), 
name));
-    }
-
     /**
      * Returns the value for the property of the given name if that property 
exists, or a fallback value otherwise.
      *
@@ -322,6 +309,20 @@ final class SparseFeature extends AbstractFeature 
implements CloneAccess {
         }
     }
 
+    /**
+     * Returns the explicit or default value of a characteristic of a property.
+     * Overridden for skipping the creation of property instances when there 
is no characteristic.
+     */
+    @Override
+    public Optional<?> getCharacteristicValue(final String property, final 
String characteristic)
+            throws PropertyNotFoundException
+    {
+        if (valuesKind != VALUES) {
+            return super.getCharacteristicValue(property, characteristic);
+        }
+        return Optional.empty();
+    }
+
     /**
      * Verifies if all current properties met the constraints defined by the 
feature type. This method returns
      * {@linkplain 
org.apache.sis.metadata.iso.quality.DefaultDataQuality#getReports() reports} 
for all invalid
@@ -330,11 +331,11 @@ final class SparseFeature extends AbstractFeature 
implements CloneAccess {
     @Override
     public DataQuality quality() {
         if (valuesKind == VALUES) {
-            final Validator v = new Validator(ScopeCode.FEATURE);
+            final var validator = new Validator(ScopeCode.FEATURE);
             for (final Map.Entry<String, Integer> entry : indices.entrySet()) {
-                v.validateAny(type.getProperty(entry.getKey()), 
properties.get(entry.getValue()));
+                validator.validateAny(type.getProperty(entry.getKey()), 
properties.get(entry.getValue()));
             }
-            return v.quality;
+            return validator.quality;
         }
         // Slower path when there is a possibility that user overridden the 
Property.quality() methods.
         return super.quality();
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/AttributeConvention.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/AttributeConvention.java
index 730b971643..c927308d49 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/AttributeConvention.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/AttributeConvention.java
@@ -24,6 +24,7 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.util.Static;
 import org.apache.sis.util.iso.Names;
 import org.apache.sis.feature.Features;
+import org.apache.sis.feature.AbstractFeature;
 import org.apache.sis.geometry.wrapper.Geometries;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
@@ -138,7 +139,7 @@ public final class AttributeConvention extends Static {
      * <p>The {@linkplain 
org.apache.sis.feature.DefaultAttributeType#getValueClass() value class} should 
be
      * {@link org.opengis.referencing.crs.CoordinateReferenceSystem}.</p>
      *
-     * @see #getCRSCharacteristic(Property)
+     * @see #getCRSCharacteristic(Feature, String)
      */
     public static final ScopedName CRS_CHARACTERISTIC = 
Names.createScopedName(SCOPE, null, "crs");
 
@@ -168,7 +169,7 @@ public final class AttributeConvention extends Static {
      * <p>The {@linkplain 
org.apache.sis.feature.DefaultAttributeType#getValueClass() value class} should 
be
      * {@link Integer}.</p>
      *
-     * @see #getMaximalLengthCharacteristic(Property)
+     * @see #getMaximalLengthCharacteristic(Feature, String)
      */
     public static final ScopedName MAXIMAL_LENGTH_CHARACTERISTIC = 
Names.createScopedName(SCOPE, null, "maximalLength");
 
@@ -299,18 +300,20 @@ public final class AttributeConvention extends Static {
     }
 
     /**
-     * Returns the Coordinate Reference Systems characteristic for the given 
attribute, or {@code null} if none.
+     * Returns the Coordinate Reference Systems characteristic for the 
specified attribute, or {@code null} if none.
      * This method gets the value or default value from the characteristic 
named {@link #CRS_CHARACTERISTIC}.
      *
-     * @param  attribute  the attribute for which to get the CRS, or {@code 
null}.
-     * @return the Coordinate Reference System characteristic of the given 
attribute, or {@code null} if none.
+     * @param  feature   the feature instance from which to get the 
<abbr>CRS</abbr> of an attribute.
+     * @param  property  name of the property for which to get the 
<abbr>CRS</abbr>.
+     * @return the Coordinate Reference System characteristic of the specified 
property, or {@code null} if none.
+     * @throws PropertyNotFoundException if the {@code property} argument is 
not the name of a property of the given feature.
      * @throws ClassCastException if {@link #CRS_CHARACTERISTIC} has been 
found but is associated
      *         to an object which is not a {@link CoordinateReferenceSystem} 
instance.
      *
      * @see 
org.apache.sis.feature.builder.AttributeTypeBuilder#setCRS(CoordinateReferenceSystem)
      */
-    public static CoordinateReferenceSystem getCRSCharacteristic(final 
Property attribute) {
-        return (CoordinateReferenceSystem) getCharacteristic(attribute, CRS);
+    public static CoordinateReferenceSystem getCRSCharacteristic(final Feature 
feature, final String property) {
+        return (CoordinateReferenceSystem) getCharacteristic(feature, 
property, CRS);
     }
 
     /**
@@ -319,8 +322,8 @@ public final class AttributeConvention extends Static {
      * If the given property is a link, then this method follows the link in 
the given feature type (if non-null).
      *
      * <p>This method should be used only when the actual property instance is 
unknown.
-     * Otherwise, {@link #getCRSCharacteristic(Property)} should be used 
because the CRS
-     * may vary for each property instance.</p>
+     * Otherwise, {@link #getCRSCharacteristic(Feature, String)} should be 
used because
+     * the <abbr>CRS</abbr> may vary for each property instance.</p>
      *
      * @param  feature    the feature type in which to follow links, or {@code 
null} if none.
      * @param  attribute  the attribute type for which to get the CRS, or 
{@code null}.
@@ -348,15 +351,17 @@ public final class AttributeConvention extends Static {
      * Returns the maximal length characteristic for the given attribute, or 
{@code null} if none.
      * This method gets the value or default value from the characteristic 
named {@link #MAXIMAL_LENGTH_CHARACTERISTIC}.
      *
-     * @param  attribute  the attribute for which to get the maximal length, 
or {@code null}.
-     * @return the maximal length characteristic of the given attribute, or 
{@code null} if none.
+     * @param  feature   the feature instance from which to get the maximal 
length of an attribute.
+     * @param  property  the name of the property for which to get the maximal 
length.
+     * @return the maximal length characteristic of the specified property, or 
{@code null} if none.
+     * @throws PropertyNotFoundException if the {@code property} argument is 
not the name of a property of the given feature.
      * @throws ClassCastException if {@link #MAXIMAL_LENGTH_CHARACTERISTIC} 
has been found but is associated
      *         to an object which is not an {@link Integer} instance.
      *
      * @see 
org.apache.sis.feature.builder.AttributeTypeBuilder#setMaximalLength(Integer)
      */
-    public static Integer getMaximalLengthCharacteristic(final Property 
attribute) {
-        return (Integer) getCharacteristic(attribute, MAXIMAL_LENGTH);
+    public static Integer getMaximalLengthCharacteristic(final Feature 
feature, final String property) {
+        return (Integer) getCharacteristic(feature, property, MAXIMAL_LENGTH);
     }
 
     /**
@@ -365,8 +370,8 @@ public final class AttributeConvention extends Static {
      * If the given property is a link, then this method follows the link in 
the given feature type (if non-null).
      *
      * <p>This method should be used only when the actual property instance is 
unknown.
-     * Otherwise, {@link #getMaximalLengthCharacteristic(Property)} should be 
used because
-     * the maximal length may vary for each property instance.</p>
+     * Otherwise, {@link #getMaximalLengthCharacteristic(Feature, String)} 
should be used
+     * because the maximal length may vary for each property instance.</p>
      *
      * @param  feature    the feature type in which to follow links, or {@code 
null} if none.
      * @param  attribute  the attribute type for which to get the maximal 
length, or {@code null}.
@@ -399,24 +404,30 @@ public final class AttributeConvention extends Static {
     }
 
     /**
-     * Fetches from the given property the value or default value of the named 
characteristic.
-     * If the given property is null, or is not an attribute, or does not have 
characteristics
+     * Fetches from the specified property the value or default value of the 
named characteristic.
+     * If the specified property is null, or is not an attribute, or does not 
have characteristics
      * of the given name, then this method returns {@code null}.
      *
-     * @param  attribute  the attribute from which to get the characteristic 
value or default value, or {@code null}.
-     * @param  name       name of the characteristic to get.
-     * @return the value or default value of the given characteristic in the 
given property, or {@code null} if none.
+     * @param  feature         the feature instance from which to get the 
characteristic.
+     * @param  property        name of the property for which to get the 
characteristic.
+     * @param  characteristic  name of the characteristic from which to get 
the value or default value.
+     * @return the value or default value of the specified characteristic in 
the specified property, or {@code null} if none.
+     * @throws PropertyNotFoundException if the {@code property} argument is 
not the name of a property of the given feature.
      */
-    private static Object getCharacteristic(final Property attribute, final 
String name) {
+    private static Object getCharacteristic(final Feature feature, final 
String property, final String characteristic) {
+        if (feature instanceof AbstractFeature) {
+            return ((AbstractFeature) 
feature).getCharacteristicValue(property, characteristic).orElse(null);
+        }
+        final Property attribute = feature.getProperty(property);
         if (attribute instanceof Attribute<?>) {
-            final Attribute<?> at = ((Attribute<?>) 
attribute).characteristics().get(name);
+            final Attribute<?> at = ((Attribute<?>) 
attribute).characteristics().get(characteristic);
             if (at != null) {
                 final Object value = at.getValue();
                 if (value != null) {
                     return value;
                 }
             }
-            final AttributeType<?> type = ((Attribute<?>) 
attribute).getType().characteristics().get(name);
+            final AttributeType<?> type = ((Attribute<?>) 
attribute).getType().characteristics().get(characteristic);
             if (type != null) {
                 return type.getDefaultValue();
             }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/internal/Node.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/internal/Node.java
index ba1ae0bdda..18788702b8 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/internal/Node.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/internal/Node.java
@@ -383,7 +383,7 @@ public abstract class Node implements Serializable {
      * Reports that an operation failed because of the given exception.
      * This method assumes that the warning occurred in a {@code test(…)} or 
{@code apply(…)} method.
      *
-     * @param  e            the exception that occurred.
+     * @param  exception    the exception that occurred.
      * @param  recoverable  {@code true} if the caller has been able to 
fallback on a default value,
      *                      or {@code false} if the caller has to return 
{@code null}.
      *
@@ -391,16 +391,16 @@ public abstract class Node implements Serializable {
      *
      * @see <a href="https://issues.apache.org/jira/browse/SIS-460";>SIS-460</a>
      */
-    protected final void warning(final Exception e, final boolean recoverable) 
{
+    protected final void warning(final Exception exception, final boolean 
recoverable) {
         final Consumer<WarningEvent> listener = WarningEvent.LISTENER.get();
         if (listener != null) {
-            listener.accept(new WarningEvent(this, e));
+            listener.accept(new WarningEvent(this, exception));
         } else {
             final String method = (this instanceof Predicate) ? "test" : 
"apply";
             if (recoverable) {
-                Logging.recoverableException(LOGGER, getClass(), method, e);
+                Logging.recoverableException(LOGGER, getClass(), method, 
exception);
             } else {
-                Logging.unexpectedException(LOGGER, getClass(), method, e);
+                Logging.unexpectedException(LOGGER, getClass(), method, 
exception);
             }
         }
     }
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 4ddd00cee5..f7c3a91a48 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
@@ -96,7 +96,7 @@ public final class AttributeTypeBuilderTest extends TestCase {
         assertEquals(10,                    attribute.getMinimumOccurs());
         assertEquals(60,                    attribute.getMaximumOccurs());
         assertTrue  
(AttributeConvention.characterizedByMaximalLength(attribute));
-        assertEquals(Integer.valueOf(80), 
AttributeConvention.getMaximalLengthCharacteristic(attribute.newInstance()));
+        assertEquals(Integer.valueOf(80), 
AttributeConvention.getMaximalLengthCharacteristic(null, attribute));
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/privy/AttributeConventionTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/privy/AttributeConventionTest.java
index c5576d1124..0e40889a81 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/privy/AttributeConventionTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/privy/AttributeConventionTest.java
@@ -76,7 +76,7 @@ public final class AttributeConventionTest extends TestCase {
         final Map<String,?> properties = Map.of(DefaultAttributeType.NAME_KEY, 
"geometry");
         var attribute = new DefaultAttributeType<>(properties, Point.class, 1, 
1, null);
         assertFalse(AttributeConvention.characterizedByCRS(attribute));
-        
assertNull(AttributeConvention.getCRSCharacteristic(attribute.newInstance()));
+        
assertFalse(attribute.characteristics().containsKey(AttributeConvention.CRS));
         /*
          * Creates an attribute associated to an attribute (i.e. a 
"characteristic") for storing
          * the Coordinate Reference System of the "geometry" attribute. Then 
test again.
@@ -87,7 +87,6 @@ public final class AttributeConventionTest extends TestCase {
 
         attribute = new DefaultAttributeType<>(properties, Point.class, 1, 1, 
null, characteristic);
         assertTrue(AttributeConvention.characterizedByCRS(attribute));
-        assertEquals(HardCodedCRS.WGS84, 
AttributeConvention.getCRSCharacteristic(attribute.newInstance()));
         assertEquals(HardCodedCRS.WGS84, 
AttributeConvention.getCRSCharacteristic(null, attribute));
         /*
          * Test again AttributeConvention.getCRSCharacteristic(…, 
PropertyType), but following link.
@@ -106,17 +105,17 @@ public final class AttributeConventionTest extends 
TestCase {
         final Map<String,?> properties = Map.of(DefaultAttributeType.NAME_KEY, 
"name");
         var attribute = new DefaultAttributeType<>(properties, String.class, 
1, 1, null);
         
assertFalse(AttributeConvention.characterizedByMaximalLength(attribute));
-        
assertNull(AttributeConvention.getMaximalLengthCharacteristic(attribute.newInstance()));
+        assertNull(AttributeConvention.getMaximalLengthCharacteristic(null, 
attribute));
         /*
          * Creates an attribute associated to an attribute (i.e. a 
"characteristic") for storing
          * the maximal length of the "name" attribute. Then test again.
          */
-        final DefaultAttributeType<Integer> characteristic = new 
DefaultAttributeType<>(
+        final var characteristic = new DefaultAttributeType<Integer>(
                 Map.of(DefaultAttributeType.NAME_KEY, 
AttributeConvention.MAXIMAL_LENGTH_CHARACTERISTIC),
                 Integer.class, 1, 1, 120);
 
         attribute = new DefaultAttributeType<>(properties, String.class, 1, 1, 
null, characteristic);
         
assertTrue(AttributeConvention.characterizedByMaximalLength(attribute));
-        assertEquals(Integer.valueOf(120), 
AttributeConvention.getMaximalLengthCharacteristic(attribute.newInstance()));
+        assertEquals(Integer.valueOf(120), 
AttributeConvention.getMaximalLengthCharacteristic(null, attribute));
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/base/FeatureSetTest.java
 
b/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/base/FeatureSetTest.java
index f76d3baef4..c90680b3d1 100644
--- 
a/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/base/FeatureSetTest.java
+++ 
b/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/base/FeatureSetTest.java
@@ -184,7 +184,7 @@ public class FeatureSetTest extends TestCase {
             }
         }
         // Convert the time vector to an array of instants.
-        final Instant[] instants = new Instant[times.length];
+        final var instants = new Instant[times.length];
         for (int i=0; i<times.length; i++) {
             instants[i] = timeOrigin.plus(times[i], ChronoUnit.MINUTES);
         }
@@ -192,11 +192,11 @@ public class FeatureSetTest extends TestCase {
          * Verify property values and characteristics.
          */
         assertEquals(identifier, instance.getPropertyValue("features"));
-        final Attribute<?> trajectory = (Attribute<?>) 
instance.getProperty("trajectory");
+        final var trajectory = (Attribute<?>) 
instance.getProperty("trajectory");
         asserLineStringEquals((Shape) trajectory.getValue(), longitudes, 
latitudes);
         assertArrayEquals(stations, ((Collection<?>) 
instance.getPropertyValue("stations")).toArray());
         assertArrayEquals(instants, 
trajectory.characteristics().get("datetimes").getValues().toArray());
-        assertInstanceOf(GeographicCRS.class, 
AttributeConvention.getCRSCharacteristic(trajectory));
+        assertInstanceOf(GeographicCRS.class, 
AttributeConvention.getCRSCharacteristic(instance, "trajectory"));
     }
 
     /**

Reply via email to