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

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

commit f636f7550492e46c26c1897d4a2688795ad034be
Merge: 2b40ccfdc4 b8b49e9bc2
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Tue Mar 12 20:06:26 2024 +0100

    Merge branch 'geoapi-3.1'.

 .../metadata/iso/distribution/DefaultMedium.java   |  2 +-
 .../apache/sis/metadata/sql/MetadataFallback.java  |  9 +-
 .../apache/sis/metadata/sql/MetadataSource.java    | 12 ++-
 .../main/org/apache/sis/util/iso/Types.java        | 68 +++++++++++++--
 .../apache/sis/xml/bind/cat/CodeListAdapter.java   |  8 +-
 .../sis/xml/bind/gco/GO_CharacterString.java       | 24 ++++--
 .../apache/sis/xml/bind/gml/CodeListAdapter.java   | 11 ++-
 .../apache/sis/xml/bind/gml/SC_VerticalCRS.java    |  2 +-
 .../sis/xml/bind/metadata/replace/SensorType.java  |  6 +-
 .../test/org/apache/sis/util/iso/TypesTest.java    | 22 ++---
 .../apache/sis/io/wkt/GeodeticObjectParser.java    |  4 +-
 .../sis/referencing/cs/DirectionAlongMeridian.java |  2 +-
 .../referencing/internal/VerticalDatumTypes.java   | 25 ++----
 .../apache/sis/storage/netcdf/MetadataReader.java  | 12 +--
 .../org/apache/sis/storage/netcdf/base/Axis.java   |  4 +-
 .../org/apache/sis/converter/StringConverter.java  | 10 +--
 .../main/org/apache/sis/util/privy/CodeLists.java  | 98 ++++++++++++++--------
 .../apache/sis/util/collection/LargeCodeList.java  |  3 +-
 .../gui/referencing/PositionableProjection.java    | 24 +++---
 .../apache/sis/gui/referencing/package-info.java   |  2 +-
 20 files changed, 225 insertions(+), 123 deletions(-)

diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataFallback.java
index 5fb4e3cf41,f2e9856607..5a97fc34f9
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataFallback.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataFallback.java
@@@ -79,8 -80,12 +80,12 @@@ final class MetadataFallback extends Me
          ArgumentChecks.ensureNonNull("type", type);
          ArgumentChecks.ensureNonEmpty("identifier", identifier);
          Object value;
 -        if (ControlledVocabulary.class.isAssignableFrom(type)) {
 +        if (CodeList.class.isAssignableFrom(type)) {
-             value = getCodeList(type, identifier);
+             try {
+                 value = getCodeList(type, identifier);
+             } catch (IllegalArgumentException e) {
+                 throw new 
MetadataStoreException(Errors.format(Errors.Keys.DatabaseError_2, type, 
identifier), e);
+             }
          } else {
              value = null;
              if (type == Citation.class) {
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataSource.java
index 048e59d276,49ad8d16ea..b49468feb2
--- 
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
@@@ -877,8 -878,12 +878,12 @@@ public class MetadataSource implements 
       */
      private Object lookup(final Class<?> type, final String identifier, 
boolean verify) throws MetadataStoreException {
          Object value;
 -        if (ControlledVocabulary.class.isAssignableFrom(type)) {
 +        if (CodeList.class.isAssignableFrom(type)) {
-             value = getCodeList(type, identifier);
+             try {
+                 value = getCodeList(type, identifier);
+             } catch (IllegalArgumentException e) {
+                 throw new 
MetadataStoreException(Errors.format(Errors.Keys.DatabaseError_2, type, 
identifier), e);
+             }
          } else {
              final CacheKey key = new CacheKey(type, identifier);
              /*
@@@ -1060,10 -1065,17 +1065,13 @@@
      /**
       * Returns the code of the given type and name. This method is defined 
for avoiding the compiler warning
       * message when the actual class is unknown (it must have been checked 
dynamically by the caller however).
+      *
+      * @return the requested code, or {@code null} if the given name is null 
or empty.
+      * @throws IllegalArgumentException if there is no value for the given 
name and the code cannot be created.
       */
      @SuppressWarnings("unchecked")
 -    static ControlledVocabulary getCodeList(final Class<?> type, final String 
name) {
 -        if (type.isEnum()) {
 -            return (ControlledVocabulary) 
CodeLists.forEnumName(type.asSubclass(Enum.class), name);
 -        } else {
 -            return CodeLists.getOrCreate(type.asSubclass(CodeList.class), 
name);
 -        }
 +    static CodeList<?> getCodeList(final Class<?> type, final String name) {
-         return Types.forCodeName(type.asSubclass(CodeList.class), name, true);
++        return CodeLists.getOrCreate(type.asSubclass(CodeList.class), name);
      }
  
      /**
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/Types.java
index f2185e3b77,ace814308b..c9de32dd5a
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/Types.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/Types.java
@@@ -465,14 -465,30 +468,18 @@@ public final class Types extends Stati
       * Note that the size of the returned array may growth between different 
invocations of this method,
       * since users can add their own codes to an existing list.
       *
 -     * <h4>Performance note</h4>
 -     * This method works with both {@link Enum} and {@link CodeList}. However 
if the type is known to be
 -     * an {@code Enum}, then the standard {@link Class#getEnumConstants()} 
method is more efficient.
 -     *
       * @param  <T>       the compile-time type given as the {@code codeType} 
parameter.
 -     * @param  codeType  the type of code list or enumeration.
 -     * @return the list of values for the given code list or enumeration, or 
an empty array if none.
 +     * @param  codeType  the type of code list.
 +     * @return the list of values for the given code list, or an empty array 
if none.
       *
       * @see Class#getEnumConstants()
+      *
+      * @deprecated This method depends on reflection, which is restricted in 
the context of Java Module System.
+      *             Instead, {@code T.values()} static methods should be 
invoked directly as much as possible.
       */
 -    @SuppressWarnings("unchecked")
+     @Deprecated(since="1.5", forRemoval=true)
 -    public static <T extends ControlledVocabulary> T[] getCodeValues(final 
Class<T> codeType) {
 -        if (CodeList.class.isAssignableFrom(codeType)) {
 -            return (T[]) CodeList.values((Class) codeType);
 -        }
 -        final T[] codes = codeType.getEnumConstants();
 -        if (codes != null) {
 -            return codes;
 -        }
 -        return (T[]) Array.newInstance(codeType, 0);
 +    public static <T extends CodeList<?>> T[] getCodeValues(final Class<T> 
codeType) {
 +        return CodeLists.values(codeType);
      }
  
      /**
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/cat/CodeListAdapter.java
index 97a095e51f,4de8e1f5e3..3ce8db0d64
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/cat/CodeListAdapter.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/cat/CodeListAdapter.java
@@@ -18,9 -18,10 +18,10 @@@ package org.apache.sis.xml.bind.cat
  
  import jakarta.xml.bind.annotation.adapters.XmlAdapter;
  import org.opengis.util.CodeList;
 +import org.apache.sis.util.iso.Types;
+ import org.apache.sis.util.privy.CodeLists;
  import org.apache.sis.xml.bind.Context;
  import org.apache.sis.xml.bind.FilterByVersion;
 -import org.apache.sis.util.iso.Types;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/TypesTest.java
index 765fbcb650,59fdada10d..76cc0f751c
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/TypesTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/TypesTest.java
@@@ -134,21 -147,18 +134,21 @@@ public final class TypesTest extends Te
       */
      @Test
      public void testForCodeName() {
-         assertSame(ImagingCondition.SEMI_DARKNESS, 
Types.forCodeName(ImagingCondition.class, "SEMI_DARKNESS", false));
-         assertSame(ImagingCondition.SEMI_DARKNESS, 
Types.forCodeName(ImagingCondition.class, "SEMIDARKNESS",  false));
-         assertSame(ImagingCondition.SEMI_DARKNESS, 
Types.forCodeName(ImagingCondition.class, "semi darkness", false));
-         assertSame(ImagingCondition.SEMI_DARKNESS, 
Types.forCodeName(ImagingCondition.class, "semi-darkness", false));
-         assertNull(Types.forCodeName(ImagingCondition.class, "darkness", 
false));
+         assertSame(ImagingCondition.SEMI_DARKNESS, 
Types.forCodeName(ImagingCondition.class, "SEMI_DARKNESS", null));
+         assertSame(ImagingCondition.SEMI_DARKNESS, 
Types.forCodeName(ImagingCondition.class, "SEMIDARKNESS",  null));
+         assertSame(ImagingCondition.SEMI_DARKNESS, 
Types.forCodeName(ImagingCondition.class, "semi darkness", null));
+         assertSame(ImagingCondition.SEMI_DARKNESS, 
Types.forCodeName(ImagingCondition.class, "semi-darkness", null));
+         assertNull(Types.forCodeName(ImagingCondition.class, "darkness", 
null));
  
-         assertSame(PixelInCell.CELL_CORNER, 
Types.forCodeName(PixelInCell.class, "cell corner", false));
-         assertSame(PixelInCell.CELL_CORNER, 
Types.forCodeName(PixelInCell.class, "cellCorner",  false));
-         assertSame(PixelInCell.CELL_CENTER, 
Types.forCodeName(PixelInCell.class, "cell center", false));
-         assertSame(PixelInCell.CELL_CENTER, 
Types.forCodeName(PixelInCell.class, "cellCenter",  false));
+         assertSame(PixelInCell.CELL_CORNER, 
Types.forCodeName(PixelInCell.class, "cell corner", null));
+         assertSame(PixelInCell.CELL_CORNER, 
Types.forCodeName(PixelInCell.class, "cellCorner",  null));
+         assertSame(PixelInCell.CELL_CENTER, 
Types.forCodeName(PixelInCell.class, "cell center", null));
+         assertSame(PixelInCell.CELL_CENTER, 
Types.forCodeName(PixelInCell.class, "cellCenter",  null));
 -        assertSame(PixelInCell.CELL_CENTER, 
Types.forCodeName(PixelInCell.class, "cell centre", null));
 -        assertSame(PixelInCell.CELL_CENTER, 
Types.forCodeName(PixelInCell.class, "cellCentre",  null));
 +
 +        if (PENDING_NEXT_GEOAPI_RELEASE) {
-             assertSame(PixelInCell.CELL_CENTER, 
Types.forCodeName(PixelInCell.class, "cell centre", false));
-             assertSame(PixelInCell.CELL_CENTER, 
Types.forCodeName(PixelInCell.class, "cellCentre",  false));
++            assertSame(PixelInCell.CELL_CENTER, 
Types.forCodeName(PixelInCell.class, "cell centre", null));
++            assertSame(PixelInCell.CELL_CENTER, 
Types.forCodeName(PixelInCell.class, "cellCentre",  null));
 +        }
      }
  
      /**
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
index c0f1fc1107,b7720a9ffd..b6de9fa530
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
@@@ -17,17 -17,21 +17,19 @@@
  package org.apache.sis.referencing.internal;
  
  import java.util.Collection;
++import java.util.function.Predicate;
  import javax.measure.Unit;
  import org.opengis.util.CodeList;
  import org.opengis.util.GenericName;
  import org.opengis.referencing.datum.VerticalDatumType;
  import org.opengis.referencing.cs.CoordinateSystemAxis;
  import org.opengis.referencing.cs.AxisDirection;
- import org.apache.sis.util.StringBuilders;
- import org.apache.sis.util.CharSequences;
  import org.apache.sis.util.Characters;
+ import org.apache.sis.util.CharSequences;
+ import org.apache.sis.util.StringBuilders;
+ import org.apache.sis.util.privy.CodeLists;
  import org.apache.sis.measure.Units;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import java.util.function.Predicate;
 -
  
  /**
   * Extensions to the standard set of {@link VerticalDatumType}.
diff --cc 
endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/CodeLists.java
index b96c511e97,323e2cb003..7843f929d2
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/CodeLists.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/CodeLists.java
@@@ -16,18 -16,22 +16,20 @@@
   */
  package org.apache.sis.util.privy;
  
 +import java.lang.reflect.Array;
  import java.lang.reflect.InvocationTargetException;
+ import java.lang.reflect.InaccessibleObjectException;
  import java.lang.reflect.UndeclaredThrowableException;
+ import java.util.function.Predicate;
  import org.opengis.util.CodeList;
  import org.apache.sis.util.CharSequences;
  import org.apache.sis.util.Characters.Filter;
+ import org.apache.sis.util.resources.Errors;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.util.ControlledVocabulary;
 -
  
  /**
   * Implementation of some {@link org.apache.sis.util.iso.Types} methods 
needed by {@code org.apache.sis.util} module.
-  * This class opportunistically implements {@link CodeList.Filter} interface, 
but this should be considered
-  * an implementation details.
 - * This class opportunistically implements {@link Predicate} interface, but 
this is an implementation details.
++ * This class opportunistically implements {@code CodeList.Filter} interface, 
but this is an implementation details.
   *
   * @author  Martin Desruisseaux (Geomatys)
   */
@@@ -43,27 -41,13 +45,29 @@@ public final class CodeLists implement
       */
      private final String codename;
  
 +    /**
 +     * {@code true} if {@link CodeList#valueOf} is allowed to create new code 
lists.
 +     */
 +    private final boolean canCreate;
 +
      /**
       * Creates a new filter for the specified code name.
+      *
+      * @param codename the name to compare during filtering operation.
       */
 -    private CodeLists(final String codename) {
 +    private CodeLists(final String codename, final boolean canCreate) {
          this.codename  = codename;
 +        this.canCreate = canCreate;
 +    }
 +
 +    /**
 +     * Returns the name of the code to create, or {@code null} if no new code 
list shall be created.
 +     *
 +     * @return the name specified at construction time.
 +     */
 +    @Override
 +    public String codename() {
 +        return canCreate ? codename : null;
      }
  
      /**
@@@ -126,32 -89,75 +109,77 @@@
              return Enum.valueOf(enumType, name);
          } catch (IllegalArgumentException e) {
              final T[] values = enumType.getEnumConstants();
-             if (values == null) {
-                 throw e;
-             }
-             for (final Enum<?> code : values) {
-                 if (accept(code.name(), name)) {
-                     return enumType.cast(code);
 -            if (values instanceof ControlledVocabulary[]) {
 -                for (final ControlledVocabulary code : 
(ControlledVocabulary[]) values) {
 -                    for (final String candidate : code.names()) {
 -                        if (accept(candidate, name)) {
 -                            return enumType.cast(code);
 -                        }
 -                    }
 -                }
 -            } else if (values != null) {
++            if (values != null) {
+                 for (final Enum<?> code : values) {
+                     if (accept(code.name(), name)) {
+                         return enumType.cast(code);
+                     }
                  }
              }
+             throw e;
          }
          return null;
      }
  
+     /**
+      * Returns the code of the given type that matches the given name.
+      *
+      * @param  <E>       the compile-time type given as the {@code codeType} 
parameter.
+      * @param  codeType  the type of code list.
+      * @param  name      the name of the code to obtain, or {@code null}.
+      * @return a code matching the given name, or {@code null} if none.
+      */
+     public static <E extends CodeList<E>> E forCodeName(final Class<E> 
codeType, String name) {
+         name = Strings.trimOrNull(name);
 -        return (name != null) ? find(codeType, new CodeLists(name)) : null;
++        return (name != null) ? CodeList.valueOf(codeType, new 
CodeLists(name, false)) : null;
+     }
+ 
+     /**
+      * Returns the code of the given type that matches the filter.
+      *
+      * @param  <E>       the compile-time type given as the {@code codeType} 
parameter.
+      * @param  codeType  the type of code list.
+      * @param  filter    the criterion for selecting a code list.
+      * @return a code matching the given name, or {@code null} if none.
+      */
+     public static <E extends CodeList<E>> E find(final Class<E> codeType, 
final Predicate<? super CodeList<?>> filter) {
 -        for (final E code : CodeList.values(codeType)) {
 -            if (filter.test(code)) {
 -                return code;
 -            }
 -        }
 -        return null;
++        return CodeList.valueOf(codeType, new CodeList.Filter() {
++            @Override public boolean accept(CodeList<?> code) {return 
filter.test(code);}
++            @Override public String codename() {return null;}
++        });
+     }
+ 
+     /**
+      * Returns the code of the given type that matches the given name, or 
creates a new code if none match the name.
+      * This method depends on reflection and should be avoided as much as 
possible.
+      * However, at this time we have no way to avoid this method completely.
+      *
+      * @param  <E>       the compile-time type given as the {@code codeType} 
parameter.
+      * @param  codeType  the type of code list.
+      * @param  name      the name of the code to obtain, or {@code null}.
+      * @return a code matching the given name, or {@code null} if the given 
name was null or blank.
+      * @throws IllegalArgumentException if no code value value matches the 
given name and new code cannot be created.
+      */
+     public static <E extends CodeList<E>> E getOrCreate(final Class<E> 
codeType, String name) {
+         name = Strings.trimOrNull(name);
+         if (name == null) {
+             return null;
+         }
 -        E code = forCodeName(codeType, name);
 -        if (code == null) try {
 -            code = codeType.cast(codeType.getMethod("valueOf", 
String.class).invoke(null, name));
++        return CodeList.valueOf(codeType, new CodeLists(name, true));
++    }
++
 +    /**
 +     * Returns all known values for the given type of code list or 
enumeration.
 +     *
 +     * @param  <T>       the compile-time type given as the {@code codeType} 
parameter.
 +     * @param  codeType  the type of code list or enumeration.
 +     * @return the list of values for the given code list or enumeration, or 
an empty array if none.
-      *
-      * @see org.apache.sis.util.iso.Types#getCodeValues(Class)
 +     */
 +    @SuppressWarnings("unchecked")
 +    public static <T extends CodeList<?>> T[] values(final Class<T> codeType) 
{
 +        Object values;
 +        try {
 +            values = codeType.getMethod("values", (Class<?>[]) 
null).invoke(null, (Object[]) null);
          } catch (InvocationTargetException e) {
              final Throwable cause = e.getCause();
              if (cause instanceof RuntimeException) {
diff --cc 
optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/PositionableProjection.java
index 61a92019c1,0f7237ee39..ab5b9b262e
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/PositionableProjection.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/PositionableProjection.java
@@@ -134,22 -126,18 +134,18 @@@ public abstract class PositionableProje
      private final short nameKey;
  
      /**
-      * Constructs an element of the given name. The new element is 
automatically added to the list
-      * returned by {@link #values()}. Subclasses shall ensure that only one 
instance is created for
-      * each value because there is no mechanism for removing previously 
created values.
+      * Constructs an element of the given name.
       *
-      * @param name  the name of the new element. This name shall not be in 
use by another element of this type.
-      */
-     protected PositionableProjection(final String name) {
-         super(name, VALUES);
-         nameKey = 0;
-     }
- 
-     /**
-      * Creates a new enumeration.
+      * <h4>Design note</h4>
+      * We do not provide public or protected constructor because code lists 
should be final,
+      * except for anonymous classes. Otherwise, {@link CodeList} constructor 
associate codes
+      * to the wrong class, as seen from the values returned by {@link 
#values(Class)}.
+      *
+      * @param name     the name of the new element. This name shall not be in 
use by another element of this type.
+      * @param nameKey  the projection name as a {@link Resources} keys.
       */
-     private PositionableProjection(final String name, final short nameKey) {
+     PositionableProjection(final String name, final short nameKey) {
 -        super(name);
 +        super(name, VALUES);
          this.nameKey = nameKey;
      }
  

Reply via email to