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 16c01f5cdf Add a mechanism for letting `MetadataStandard` know that 
the `Datum` interface implemented by `DefaultDatumEnsemble` should be ignored.
16c01f5cdf is described below

commit 16c01f5cdf40fe415196d5bd7d6d84db440c42fe
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri Jul 11 18:03:29 2025 +0200

    Add a mechanism for letting `MetadataStandard` know that the `Datum`
    interface implemented by `DefaultDatumEnsemble` should be ignored.
---
 .../org/apache/sis/metadata/MetadataStandard.java  | 67 +++++++++++-----------
 .../org/apache/sis/metadata/PropertyAccessor.java  |  4 +-
 .../sis/metadata/StandardImplementation.java       |  3 +-
 .../main/org/apache/sis/metadata/TreeNode.java     |  2 +-
 .../org/apache/sis/metadata/TreeNodeChildren.java  |  2 +-
 .../apache/sis/metadata/internal/ExcludedSet.java  |  6 +-
 .../apache/sis/metadata/privy/SecondaryTrait.java  | 45 +++++++++++++++
 .../referencing/datum/DefaultDatumEnsemble.java    |  2 +
 .../sis/util/privy/UnmodifiableArrayList.java      |  2 +-
 9 files changed, 91 insertions(+), 42 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/MetadataStandard.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/MetadataStandard.java
index 91eb62040e..6027ca5296 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/MetadataStandard.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/MetadataStandard.java
@@ -18,8 +18,7 @@ package org.apache.sis.metadata;
 
 import java.util.Set;
 import java.util.Map;
-import java.util.LinkedHashSet;
-import java.util.Collection;
+import java.util.LinkedHashMap;
 import java.util.Iterator;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentMap;
@@ -27,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.io.IOException;
 import java.io.Serializable;
 import java.io.ObjectInputStream;
+import org.apache.sis.metadata.privy.SecondaryTrait;
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.ExtendedElementInformation;
 import org.opengis.metadata.citation.Citation;
@@ -494,21 +494,29 @@ public class MetadataStandard implements Serializable {
             }
         } else {
             /*
-             * Gets every interfaces from the supplied class in declaration 
order,
-             * including the ones declared in the super-class.
+             * Get every interfaces from the supplied class in declaration 
order,
+             * including the ones declared in the super-class. The Boolean 
value
+             * tells whether the type is supported. Types associated to `FALSE`
+             * shall be ignored.
              */
-            final Set<Class<?>> interfaces = new LinkedHashSet<>();
+            final var validities = new LinkedHashMap<Class<?>, Boolean>();
+            final SecondaryTrait ignore = 
key.type.getAnnotation(SecondaryTrait.class);
+            if (ignore != null) {
+                validities.put(ignore.value(), Boolean.FALSE);
+            }
             for (Class<?> t=key.type; t!=null; t=t.getSuperclass()) {
-                getInterfaces(t, key.propertyType, interfaces);
+                getInterfaces(t, key.propertyType, validities);
             }
             /*
-             * If we found more than one interface, removes the
-             * ones that are sub-interfaces of the other.
+             * Remove all unsupported types. Then, if we found more than one 
supported
+             * interface, remove the ones that are sub-interfaces of the other.
              */
+            validities.values().removeIf((isSupported) -> !isSupported);
+            final Set<Class<?>> interfaces = validities.keySet();
             for (final Iterator<Class<?>> it=interfaces.iterator(); 
it.hasNext();) {
                 final Class<?> candidate = it.next();
-                for (final Class<?> child : interfaces) {
-                    if (candidate != child && 
candidate.isAssignableFrom(child)) {
+                for (final Class<?> other : interfaces) {
+                    if (candidate != other && 
candidate.isAssignableFrom(other)) {
                         it.remove();
                         break;
                     }
@@ -522,7 +530,7 @@ public class MetadataStandard implements Serializable {
                 }
                 /*
                  * Found more than one interface; we don't know which one to 
pick.
-                 * Returns `null` for now; the caller will thrown an exception.
+                 * Returns `null` for now; the caller will throw an exception.
                  */
             } else if (IMPLEMENTATION_CAN_ALTER_API) {
                 /*
@@ -543,8 +551,9 @@ public class MetadataStandard implements Serializable {
     }
 
     /**
-     * Puts every interfaces for the given type in the specified collection.
+     * Puts every interfaces for the given type in the specified map.
      * This method invokes itself recursively for scanning parent interfaces.
+     * The keys tell whether the interface is supported. validities
      *
      * <p>If the given class is the return value of a property, then the type 
of that property should be specified
      * in the {@code propertyType} argument. This information allows this 
method to take in account only the types
@@ -554,14 +563,10 @@ public class MetadataStandard implements Serializable {
      *
      * @see Classes#getAllInterfaces(Class)
      */
-    private void getInterfaces(final Class<?> type, final Class<?> 
propertyType, final Collection<Class<?>> interfaces) {
+    private void getInterfaces(final Class<?> type, final Class<?> 
propertyType, final Map<Class<?>, Boolean> validities) {
         for (final Class<?> candidate : type.getInterfaces()) {
-            if (propertyType.isAssignableFrom(candidate)) {
-                if (isSupported(candidate.getName())) {
-                    interfaces.add(candidate);
-                }
-                getInterfaces(candidate, propertyType, interfaces);
-            } else if (IMPLEMENTATION_CAN_ALTER_API) {
+            final boolean recursive = propertyType.isAssignableFrom(candidate);
+            if (recursive || (IMPLEMENTATION_CAN_ALTER_API && 
isPendingAPI(propertyType))) {
                 /*
                  * If a GeoAPI interface is not assignable to the property 
type, maybe it is because the property type
                  * did not existed at the time current GeoAPI version was 
published. In such case, the implementation
@@ -569,11 +574,8 @@ public class MetadataStandard implements Serializable {
                  * we skip the `isAssignableFrom` check, but without recursive 
addition of parent interfaces since we
                  * would not know when to stop.
                  */
-                if (isPendingAPI(propertyType)) {
-                    if (isSupported(candidate.getName())) {
-                        interfaces.add(candidate);
-                    }
-                    // No recursive call here.
+                if (validities.putIfAbsent(candidate, 
isSupported(candidate.getName())) == null && recursive) {
+                    getInterfaces(candidate, propertyType, validities);
                 }
             }
         }
@@ -1062,14 +1064,15 @@ public class MetadataStandard implements Serializable {
             return false;
         }
         final PropertyAccessor accessor = getAccessor(new CacheKey(type1), 
true);
-        if (type1 != type2 && (!accessor.type.isAssignableFrom(type2)
-                || accessor.type != getAccessor(new CacheKey(type2), 
false).type))
-        {
-            /*
-             * Note: the check for (accessor.type != getAccessor(…).type) 
would have been enough, but checking
-             * for isAssignableFrom(…) first can avoid the (relatively costly) 
creation of new PropertyAccessor.
-             */
-            return false;
+        if (type1 != type2) {
+            // Not strictly necessary, but can avoid the relatively costly 
creation of new `PropertyAccessor`.
+            if (!accessor.type.isAssignableFrom(type2)) {
+                return false;
+            }
+            // The real condition.
+            if (accessor.type != getAccessor(new CacheKey(type2), false).type) 
{
+                return false;
+            }
         }
         /*
          * At this point, we have to perform the actual property-by-property 
comparison.
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 45382ab1a4..9b698f9533 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
@@ -287,7 +287,7 @@ class PropertyAccessor {
                 final int lo = name.codePointAt(base);
                 final int up = Character.toUpperCase(lo);
                 final int length = name.length();
-                final StringBuilder buffer = new StringBuilder(length - base + 
5).append(SET);
+                final var buffer = new StringBuilder(length - base + 
5).append(SET);
                 if (lo != up) {
                     buffer.appendCodePoint(up).append(name, base + 
Character.charCount(lo), length);
                 } else {
@@ -1319,7 +1319,7 @@ class PropertyAccessor {
      */
     @Override
     public String toString() {
-        final StringBuilder buffer = new StringBuilder(60);
+        final var buffer = new StringBuilder(60);
         buffer.append("PropertyAccessor[").append(standardCount).append(" 
getters");
         final int extra = allCount - standardCount;
         if (extra != 0) {
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/StandardImplementation.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/StandardImplementation.java
index f60fd511ab..68acb877f5 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/StandardImplementation.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/StandardImplementation.java
@@ -20,7 +20,6 @@ import java.util.Map;
 import java.util.IdentityHashMap;
 import java.util.logging.Logger;
 import java.io.ObjectStreamException;
-import java.util.concurrent.ConcurrentHashMap;
 import org.opengis.annotation.UML;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.logging.Logging;
@@ -186,7 +185,7 @@ final class StandardImplementation extends MetadataStandard 
{
          * newer version of the Apache SIS library. The newer version could 
contain constants
          * not yet declared in this older SIS version, so we have to use this 
instance.
          */
-        implementations = new ConcurrentHashMap<>();
+        implementations = new IdentityHashMap<>();
         return this;
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNode.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNode.java
index 1f40818f3b..051be71fe1 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNode.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNode.java
@@ -1077,7 +1077,7 @@ class TreeNode implements Node {
      */
     @Override
     public final String toString() {
-        final StringBuilder buffer = new StringBuilder(60);
+        final var buffer = new StringBuilder(60);
         appendStringTo(buffer);
         return buffer.toString();
     }
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNodeChildren.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNodeChildren.java
index 1be21c0771..d3b480119a 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNodeChildren.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNodeChildren.java
@@ -607,7 +607,7 @@ final class TreeNodeChildren extends 
AbstractCollection<TreeTable.Node> {
             return false;
         }
         // Conversion attempt happen in the PropertyAccessor.set(…) method.
-        final Boolean changed = (Boolean) accessor.set(index, metadata, value, 
PropertyAccessor.APPEND);
+        final var changed = (Boolean) accessor.set(index, metadata, value, 
PropertyAccessor.APPEND);
         if (changed == null) {
             throw new 
IllegalStateException(Errors.format(Errors.Keys.ValueAlreadyDefined_1,
                     accessor.name(index, KeyNamePolicy.UML_IDENTIFIER)));
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/ExcludedSet.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/ExcludedSet.java
index c23df8037a..12b9eeafe1 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/ExcludedSet.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/ExcludedSet.java
@@ -24,9 +24,9 @@ import org.apache.sis.util.resources.Errors;
 
 
 /**
- * A unmodifiable empty set with a customized exception message thrown by the 
{@link #add(Object)}
- * method. This set is used only for mutually exclusive properties, when a 
collection cannot have
- * elements because the other property is set.
+ * A unmodifiable empty set with a customized exception message thrown by the 
{@link #add(Object)} method.
+ * This set is used only for mutually exclusive properties, when a collection 
cannot have elements because
+ * the other property is set.
  *
  * @author  Martin Desruisseaux (Geomatys)
  *
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/SecondaryTrait.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/SecondaryTrait.java
new file mode 100644
index 0000000000..8c267dd9e3
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/SecondaryTrait.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.metadata.privy;
+
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+/**
+ * Indicates that an interface implemented by a class is considered less 
significant compared to the primary interface.
+ * This annotation is used when a class implements two or more interfaces, but 
some of those interfaces can be ignored.
+ * This information is needed for identifying the main interface in metadata.
+ *
+ * <h2>Design note</h2>
+ * {@link ElementType#TYPE_USE} would be more appropriate. However, in the way 
that the metadata module currently uses
+ * this interface, an annotation on the class is more convenient (more 
straightforward code).
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SecondaryTrait {
+    /**
+     * The interface which is less significant compared to the primary 
interface.
+     *
+     * @return the less significant interface.
+     */
+    Class<?> value();
+}
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultDatumEnsemble.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultDatumEnsemble.java
index 8920687048..1e831e468d 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultDatumEnsemble.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultDatumEnsemble.java
@@ -45,6 +45,7 @@ import org.apache.sis.referencing.GeodeticException;
 import org.apache.sis.referencing.IdentifiedObjects;
 import org.apache.sis.referencing.internal.Resources;
 import org.apache.sis.referencing.privy.WKTKeywords;
+import org.apache.sis.metadata.privy.SecondaryTrait;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.Utilities;
@@ -82,6 +83,7 @@ import org.opengis.referencing.datum.RealizationMethod;
  *
  * @since 1.5
  */
+@SecondaryTrait(Datum.class)
 public class DefaultDatumEnsemble<D extends Datum> extends 
AbstractIdentifiedObject implements DatumEnsemble<D>, Datum {
     /**
      * Serial number for inter-operability with different versions.
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/UnmodifiableArrayList.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/UnmodifiableArrayList.java
index be13369e4e..5bc007c279 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/UnmodifiableArrayList.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/UnmodifiableArrayList.java
@@ -398,7 +398,7 @@ public class UnmodifiableArrayList<E> extends 
AbstractList<E> implements RandomA
                 /*
                  * We are cheating here since the array component may not be 
assignable to T.
                  * But if this assumption is wrong, then the call to 
System.arraycopy(…) later
-                 * will thrown an ArrayStoreException, which is the exception 
type required by
+                 * will throw an ArrayStoreException, which is the exception 
type required by
                  * the Collection.toArray(T[]) javadoc.
                  */
                 dest = (T[]) 
Array.newInstance(dest.getClass().getComponentType(), size);

Reply via email to