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 dca5af8fc9 Update for a change in the way that CodeList are
constructed in GeoAPI. https://github.com/opengeospatial/geoapi/issues/91
dca5af8fc9 is described below
commit dca5af8fc9a814c96f65c11b408c66616ee7c325
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue Mar 12 17:24:36 2024 +0100
Update for a change in the way that CodeList are constructed in GeoAPI.
https://github.com/opengeospatial/geoapi/issues/91
---
.../apache/sis/metadata/iso/legacy/MediumName.java | 77 ++++++---------
.../apache/sis/metadata/sql/MetadataFallback.java | 9 +-
.../apache/sis/metadata/sql/MetadataSource.java | 14 ++-
.../main/org/apache/sis/util/iso/Types.java | 78 +++++++++++++--
.../apache/sis/xml/bind/cat/CodeListAdapter.java | 9 +-
.../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 | 49 ++--------
.../apache/sis/metadata/TreeNodeChildrenTest.java | 18 ++--
.../metadata/iso/citation/DefaultContactTest.java | 2 +-
.../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 | 7 +-
.../apache/sis/storage/netcdf/MetadataReader.java | 12 ++-
.../org/apache/sis/storage/netcdf/base/Axis.java | 4 +-
.../org/apache/sis/converter/StringConverter.java | 10 +-
.../apache/sis/util/collection/CodeListSet.java | 5 +-
.../main/org/apache/sis/util/privy/CodeLists.java | 106 ++++++++++++---------
.../sis/util/collection/CodeListSetTest.java | 2 +-
.../apache/sis/util/collection/LargeCodeList.java | 31 +++---
geoapi/snapshot | 2 +-
.../gui/referencing/PositionableProjection.java | 48 +++-------
.../apache/sis/gui/referencing/package-info.java | 2 +-
25 files changed, 290 insertions(+), 260 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/legacy/MediumName.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/legacy/MediumName.java
index 14d6ad1afd..6901065c3c 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/legacy/MediumName.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/legacy/MediumName.java
@@ -16,15 +16,12 @@
*/
package org.apache.sis.metadata.iso.legacy;
-import java.util.List;
-import java.util.ArrayList;
import org.opengis.annotation.UML;
import org.opengis.metadata.citation.Citation;
import org.opengis.util.CodeList;
import org.opengis.util.InternationalString;
import static org.opengis.annotation.Obligation.*;
import static org.opengis.annotation.Specification.*;
-import org.apache.sis.util.privy.CodeLists;
import org.apache.sis.util.iso.Types;
@@ -42,84 +39,86 @@ public final class MediumName extends CodeList<MediumName>
implements Citation {
/** Serial number for compatibility with different versions. */
private static final long serialVersionUID = 7157038832444373933L;
- /** List of all enumerations of this type. */
- private static final List<MediumName> VALUES = new ArrayList<>(18);
+ /*
+ * We need to construct values with `valueOf(String)` instead of the
constructor
+ * because this package is not exported to GeoAPI. See `CodeList` class
javadoc.
+ */
/** Read-only optical disk. */
@UML(identifier="cdRom", obligation=CONDITIONAL, specification=ISO_19115)
- public static final MediumName CD_ROM = new MediumName("CD_ROM");
+ public static final MediumName CD_ROM = valueOf("CD_ROM");
/** Digital versatile disk. */
@UML(identifier="dvd", obligation=CONDITIONAL, specification=ISO_19115)
- public static final MediumName DVD = new MediumName("DVD");
+ public static final MediumName DVD = valueOf("DVD");
/** Digital versatile disk digital versatile disk, read only. */
@UML(identifier="dvdRom", obligation=CONDITIONAL, specification=ISO_19115)
- public static final MediumName DVD_ROM = new MediumName("DVD_ROM");
+ public static final MediumName DVD_ROM = valueOf("DVD_ROM");
/** 3½ inch magnetic disk. */
@UML(identifier="3halfInchFloppy", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName FLOPPY_3_HALF_INCH = new
MediumName("FLOPPY_3_HALF_INCH");
+ public static final MediumName FLOPPY_3_HALF_INCH =
valueOf("FLOPPY_3_HALF_INCH");
/** 5¼ inch magnetic disk. */
@UML(identifier="5quarterInchFloppy", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName FLOPPY_5_QUARTER_INCH = new
MediumName("FLOPPY_5_QUARTER_INCH");
+ public static final MediumName FLOPPY_5_QUARTER_INCH =
valueOf("FLOPPY_5_QUARTER_INCH");
/** 7 track magnetic tape. */
@UML(identifier="7trackTape", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName TAPE_7_TRACK = new
MediumName("TAPE_7_TRACK");
+ public static final MediumName TAPE_7_TRACK = valueOf("TAPE_7_TRACK");
/** 9 track magnetic tape. */
@UML(identifier="9trackTape", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName TAPE_9_TRACK = new
MediumName("TAPE_9_TRACK");
+ public static final MediumName TAPE_9_TRACK = valueOf("TAPE_9_TRACK");
/** 3480 cartridge tape drive. */
@UML(identifier="3480Cartridge", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName CARTRIDGE_3480 = new
MediumName("CARTRIDGE_3480");
+ public static final MediumName CARTRIDGE_3480 = valueOf("CARTRIDGE_3480");
/** 3490 cartridge tape drive. */
@UML(identifier="3490Cartridge", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName CARTRIDGE_3490 = new
MediumName("CARTRIDGE_3490");
+ public static final MediumName CARTRIDGE_3490 = valueOf("CARTRIDGE_3490");
/** 3580 cartridge tape drive. */
@UML(identifier="3580Cartridge", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName CARTRIDGE_3580 = new
MediumName("CARTRIDGE_3580");
+ public static final MediumName CARTRIDGE_3580 = valueOf("CARTRIDGE_3580");
/** 4 millimetre magnetic tape. */
@UML(identifier="4mmCartridgeTape", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName CARTRIDGE_TAPE_4mm = new
MediumName("CARTRIDGE_TAPE_4mm");
+ public static final MediumName CARTRIDGE_TAPE_4mm =
valueOf("CARTRIDGE_TAPE_4mm");
/** 8 millimetre magnetic tape. */
@UML(identifier="8mmCartridgeTape", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName CARTRIDGE_TAPE_8mm = new
MediumName("CARTRIDGE_TAPE_8mm");
+ public static final MediumName CARTRIDGE_TAPE_8mm =
valueOf("CARTRIDGE_TAPE_8mm");
/** ¼ inch magnetic tape. */
@UML(identifier="1quarterInchCartridgeTape", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName CARTRIDGE_TAPE_1_QUARTER_INCH = new
MediumName("CARTRIDGE_TAPE_1_QUARTER_INCH");
+ public static final MediumName CARTRIDGE_TAPE_1_QUARTER_INCH =
valueOf("CARTRIDGE_TAPE_1_QUARTER_INCH");
/** Half inch cartridge streaming tape drive. */
@UML(identifier="digitalLinearTape", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName DIGITAL_LINEAR_TAPE = new
MediumName("DIGITAL_LINEAR_TAPE");
+ public static final MediumName DIGITAL_LINEAR_TAPE =
valueOf("DIGITAL_LINEAR_TAPE");
/** Direct computer linkage. */
@UML(identifier="onLine", obligation=CONDITIONAL, specification=ISO_19115)
- public static final MediumName ON_LINE = new MediumName("ON_LINE");
+ public static final MediumName ON_LINE = valueOf("ON_LINE");
/** Linkage through a satellite communication system. */
@UML(identifier="satellite", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName SATELLITE = new MediumName("SATELLITE");
+ public static final MediumName SATELLITE = valueOf("SATELLITE");
/** Communication through a telephone network. */
@UML(identifier="telephoneLink", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName TELEPHONE_LINK = new
MediumName("TELEPHONE_LINK");
+ public static final MediumName TELEPHONE_LINK = valueOf("TELEPHONE_LINK");
/** Pamphlet or leaflet giving descriptive information. */
@UML(identifier="hardcopy", obligation=CONDITIONAL,
specification=ISO_19115)
- public static final MediumName HARDCOPY = new MediumName("HARDCOPY");
+ public static final MediumName HARDCOPY = valueOf("HARDCOPY");
/** Constructs an element of the given name. */
private MediumName(final String name) {
- super(name, VALUES);
+ super(name);
}
/**
@@ -127,41 +126,19 @@ public final class MediumName extends
CodeList<MediumName> implements Citation {
*
* @return the list of codes declared in the current JVM.
*/
- public static MediumName[] values() {
- synchronized (VALUES) {
- return VALUES.toArray(MediumName[]::new);
- }
- }
-
- /**
- * Returns the list of codes of the same kind as this code list element.
- *
- * @return all code {@linkplain #values() values} for this code list.
- */
@Override
public MediumName[] family() {
- return values();
- }
-
- /**
- * Disables the search for UML identifiers because we do not export this
package to GeoAPI.
- *
- * @return {@code null}.
- */
- @Override
- public String identifier() {
- return null;
+ return values(MediumName.class);
}
/**
- * Returns the medium name that matches the given string, or {@code null}
if none match it.
- * Contrarily to non-deprecated code list, this method does not create a
new code if none match the given name.
+ * Returns the medium name that matches the given string, or a new one if
none match it.
*
* @param code the name of the code to fetch or to create.
* @return a code matching the given name, or {@code null}.
*/
public static MediumName valueOf(final String code) {
- return CodeLists.forName(MediumName.class, code, false);
+ return valueOf(MediumName.class, code, MediumName::new).get();
}
/**
@@ -177,7 +154,7 @@ public final class MediumName extends CodeList<MediumName>
implements Citation {
if (citation != null) {
final InternationalString title = citation.getTitle();
if (title != null) {
- return valueOf(title.toString());
+ return Types.forCodeName(MediumName.class, title.toString(),
null);
}
}
return null;
diff --git
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
index b512642248..5ce03ca4ea 100644
---
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
@@ -25,6 +25,7 @@ import org.apache.sis.metadata.iso.citation.DefaultCitation;
import org.apache.sis.metadata.iso.citation.DefaultOrganisation;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.privy.Constants;
+import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.iso.Types;
import org.apache.sis.xml.NilReason;
@@ -75,12 +76,16 @@ final class MetadataFallback extends MetadataSource {
* @return an implementation of the required interface, or the code list
element.
*/
@Override
- public <T> T lookup(final Class<T> type, final String identifier) {
+ public <T> T lookup(final Class<T> type, final String identifier) throws
MetadataStoreException {
ArgumentChecks.ensureNonNull("type", type);
ArgumentChecks.ensureNonEmpty("identifier", identifier);
Object value;
if (ControlledVocabulary.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 --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 01e8449b99..49ad8d16ea 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
@@ -67,6 +67,7 @@ import org.apache.sis.util.UnconvertibleObjectException;
import org.apache.sis.util.Exceptions;
import org.apache.sis.util.Classes;
import org.apache.sis.util.privy.Strings;
+import org.apache.sis.util.privy.CodeLists;
import org.apache.sis.util.privy.CollectionsExt;
import org.apache.sis.util.privy.UnmodifiableArrayList;
import org.apache.sis.util.collection.Containers;
@@ -878,7 +879,11 @@ public class MetadataSource implements AutoCloseable {
private Object lookup(final Class<?> type, final String identifier,
boolean verify) throws MetadataStoreException {
Object value;
if (ControlledVocabulary.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,13 +1065,16 @@ public class MetadataSource implements AutoCloseable {
/**
* 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)
Types.forEnumName(type.asSubclass(Enum.class), name);
+ return (ControlledVocabulary)
CodeLists.forEnumName(type.asSubclass(Enum.class), name);
} else {
- return Types.forCodeName(type.asSubclass(CodeList.class), name,
true);
+ return CodeLists.getOrCreate(type.asSubclass(CodeList.class),
name);
}
}
diff --git
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
index b175797473..ace814308b 100644
---
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
@@ -26,7 +26,9 @@ import java.util.IllformedLocaleException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.LogRecord;
+import java.util.function.Function;
import java.io.IOException;
+import java.lang.reflect.Array;
import org.opengis.annotation.UML;
import org.opengis.util.CodeList;
import org.opengis.util.InternationalString;
@@ -43,6 +45,7 @@ import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.resources.Messages;
import org.apache.sis.util.collection.BackingStoreException;
import org.apache.sis.util.privy.CodeLists;
+import org.apache.sis.util.privy.Strings;
import org.apache.sis.pending.jdk.JDK19;
import org.apache.sis.system.Modules;
@@ -98,7 +101,7 @@ import org.opengis.util.ControlledVocabulary;
* }
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 1.4
+ * @version 1.5
* @since 0.3
*/
public final class Types extends Static {
@@ -471,9 +474,21 @@ public final class Types extends Static {
* @return the list of values for the given code list or enumeration, 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) {
- return CodeLists.values(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);
}
/**
@@ -579,8 +594,13 @@ public final class Types extends Static {
*
* @since 0.5
*/
+ @OptionalCandidate
public static <T extends Enum<T>> T forEnumName(final Class<T> enumType,
String name) {
- return CodeLists.forName(enumType, name);
+ try {
+ return CodeLists.forEnumName(enumType, name);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
}
/**
@@ -594,6 +614,47 @@ public final class Types extends Static {
* Spaces and punctuation characters like {@code '_'} and {@code
'-'} are ignored.</li>
* </ul>
*
+ * If no match is found, then a new code is created only if the {@code
creator} argument is non-null.
+ * That argument should be a lambda function to the {@code
valueOf(String)} method of the code list class.
+ * Example:
+ *
+ * {@snippet lang="java" :
+ * AxisDirection dir = Types.forCodeName(AxisDirection.class, name,
AxisDirection::valueOf);
+ * }
+ *
+ * If the {@code constructor} is null and no existing code matches the
given name,
+ * then this method returns {@code null}.
+ *
+ * @param <T> 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}.
+ * @param constructor the constructor to use if a new code needs to be
created,
+ * or {@code null} for not creating any new code.
+ * @return a code matching the given name, or {@code null} if the name is
null
+ * or if no matching code is found and {@code constructor} is
{@code null}.
+ *
+ * @see #getCodeName(ControlledVocabulary)
+ * @see CodeList#valueOf(Class, String, Function)
+ *
+ * @since 1.5
+ */
+ @OptionalCandidate
+ public static <T extends CodeList<T>> T forCodeName(final Class<T>
codeType, String name,
+ final Function<? super String, ? extends T> constructor)
+ {
+ name = Strings.trimOrNull(name);
+ if (name == null) {
+ return null; // Avoid initialization of the <T> class.
+ }
+ T code = CodeLists.forCodeName(codeType, name);
+ if (code == null && constructor != null) {
+ code = constructor.apply(name);
+ }
+ return code;
+ }
+
+ /**
+ * Returns the code of the given type that matches the given name, or
optionally returns a new one.
* If no match is found, then a new code is created only if the {@code
canCreate} argument is {@code true}.
* Otherwise this method returns {@code null}.
*
@@ -604,11 +665,16 @@ public final class Types extends Static {
* @return a code matching the given name, or {@code null} if the name is
null
* or if no matching code is found and {@code canCreate} is {@code
false}.
*
- * @see #getCodeName(ControlledVocabulary)
- * @see CodeList#valueOf(Class, String)
+ * @deprecated This method depends on reflection, which is restricted in
the context of Java Module System.
+ * Replaced by {@link #forCodeName(Class, String, Function)}.
*/
+ @Deprecated(since="1.5", forRemoval=true)
public static <T extends CodeList<T>> T forCodeName(final Class<T>
codeType, String name, final boolean canCreate) {
- return CodeLists.forName(codeType, name, canCreate);
+ if (canCreate) {
+ return CodeLists.getOrCreate(codeType, name);
+ } else {
+ return forCodeName(codeType, name, null);
+ }
}
/**
diff --git
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
index e253257fb2..9adc3ef22a 100644
---
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,7 +18,7 @@ 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;
@@ -111,7 +111,12 @@ public abstract class CodeListAdapter<ValueType extends
CodeListAdapter<ValueTyp
*/
@Override
public final BoundType unmarshal(final ValueType adapter) {
- return (adapter != null) ? Types.forCodeName(getCodeListClass(),
adapter.identifier.toString(), true) : null;
+ if (adapter != null) try {
+ return CodeLists.getOrCreate(getCodeListClass(),
adapter.identifier.toString());
+ } catch (RuntimeException e) {
+ Context.warningOccured(Context.current(), CodeListAdapter.class,
"unmarshal", e, true);
+ }
+ return null;
}
/**
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gco/GO_CharacterString.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gco/GO_CharacterString.java
index a28472414f..a6c39e4cc2 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gco/GO_CharacterString.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gco/GO_CharacterString.java
@@ -37,6 +37,7 @@ import org.apache.sis.xml.privy.LegacyNamespaces;
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.Workaround;
import org.apache.sis.util.iso.Types;
+import org.apache.sis.util.privy.CodeLists;
import org.apache.sis.util.resources.IndexedResourceBundle;
import org.apache.sis.util.resources.Messages;
import org.apache.sis.util.resources.Errors;
@@ -294,17 +295,20 @@ public class GO_CharacterString {
if (ct != null && CodeList.class.isAssignableFrom(ct)) {
final String attribute =
e.getAttribute("codeListValue").trim();
if (!attribute.isEmpty()) {
- @SuppressWarnings("unchecked")
- final CodeList<?> c = Types.forCodeName((Class) ct,
attribute, true);
- text = Types.getCodeTitle(c);
- type = ENUM;
+ try {
+ @SuppressWarnings("unchecked")
+ final CodeList<?> c = CodeLists.getOrCreate((Class)
ct, attribute);
+ text = Types.getCodeTitle(c);
+ type = ENUM;
+ } catch (RuntimeException ex) {
+ Context.warningOccured(Context.current(),
GO_CharacterString.class, "setCodeList", ex, true);
+ }
return;
- } else {
- resources = Errors.class;
- errorKey = Errors.Keys.MissingOrEmptyAttribute_2;
- args = new Object[2];
- args[1] = "codeListValue";
}
+ resources = Errors.class;
+ errorKey = Errors.Keys.MissingOrEmptyAttribute_2;
+ args = new Object[2];
+ args[1] = "codeListValue";
} else {
resources = Messages.class;
errorKey = Messages.Keys.UnknownCodeList_1;
@@ -323,6 +327,7 @@ public class GO_CharacterString {
* @return the character sequence for this {@code <gco:CharacterString>}.
*/
protected CharSequence toCharSequence() {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
final CharSequence text = CharSequences.trimWhitespaces(this.text);
if (text != null && (text.length() != 0 || text instanceof Anchor)) {
// Anchor may contain attributes.
return text;
@@ -343,6 +348,7 @@ public class GO_CharacterString {
*/
@Override
public final String toString() {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
final CharSequence text = this.text;
return (text != null) ? text.toString() : null; // We really want
to return null here.
}
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/CodeListAdapter.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/CodeListAdapter.java
index af71e0654e..b3ee0ececb 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/CodeListAdapter.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/CodeListAdapter.java
@@ -21,6 +21,8 @@ import jakarta.xml.bind.annotation.XmlAttribute;
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;
/**
@@ -84,11 +86,16 @@ public abstract class CodeListAdapter<BoundType extends
CodeList<BoundType>> ext
* contain the value. JAXB calls automatically this method at
unmarshalling time.
*
* @param identifier the code space and identifier.
- * @return a code list which represents the GML value.
+ * @return a code list which represents the GML value, or {@code null}.
*/
@Override
public final BoundType unmarshal(final Value identifier) {
- return (identifier != null) ? Types.forCodeName(getCodeListClass(),
identifier.value, true) : null;
+ if (identifier != null) try {
+ return CodeLists.getOrCreate(getCodeListClass(), identifier.value);
+ } catch (RuntimeException e) {
+ Context.warningOccured(Context.current(), CodeListAdapter.class,
"unmarshal", e, true);
+ }
+ return null;
}
/**
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/SC_VerticalCRS.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/SC_VerticalCRS.java
index 253ff231b6..e5bc159a05 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/SC_VerticalCRS.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/SC_VerticalCRS.java
@@ -50,7 +50,7 @@ import org.apache.sis.util.resources.Errors;
* }
* }
*
- * Next, the module shall provides the following:
+ * Next, the module shall provide the following:
* <ul>
* <li>The path to {@code MyClass} shall be provided in the {@code
module-info.java} file
* as a {@code org.apache.sis.xml.bind.AdapterReplacement} service.</li>
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/SensorType.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/SensorType.java
index 74bed1e955..2c96b8d2af 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/SensorType.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/SensorType.java
@@ -16,8 +16,6 @@
*/
package org.apache.sis.xml.bind.metadata.replace;
-import java.util.List;
-import java.util.ArrayList;
import org.opengis.annotation.UML;
import org.opengis.annotation.Specification;
import org.opengis.util.CodeList;
@@ -37,72 +35,43 @@ public final class SensorType extends CodeList<SensorType> {
*/
private static final long serialVersionUID = 3510875680392393838L;
- /**
- * List of all enumerations of this type.
- * Must be declared before any enum declaration.
+ /*
+ * We need to construct values with `valueOf(String)` instead of the
constructor
+ * because this package is not exported to GeoAPI. See `CodeList` class
javadoc.
*/
- private static final List<SensorType> VALUES = new ArrayList<>();
/**
* The sensor is a radiometer.
*/
- public static final SensorType RADIOMETER = new SensorType("RADIOMETER");
+ public static final SensorType RADIOMETER = valueOf("RADIOMETER");
/**
- * Constructs an element of the given name. The new element is
- * automatically added to the list returned by {@link #values()}.
+ * Constructs an element of the given name.
*
* @param name the name of the new element.
* This name must not be in use by another element of this type.
*/
private SensorType(final String name) {
- super(name, VALUES);
- }
-
- /**
- * Returns the list of {@code SensorType}s.
- *
- * @return the list of codes declared in the current JVM.
- */
- public static SensorType[] values() {
- synchronized (VALUES) {
- return VALUES.toArray(SensorType[]::new);
- }
- }
-
- /**
- * Disables the search for UML identifiers because we do not export this
package to GeoAPI.
- *
- * @return {@code null}.
- */
- @Override
- public String identifier() {
- return null;
+ super(name);
}
/**
* Returns the list of codes of the same kind as this code list element.
- * Invoking this method is equivalent to invoking {@link #values()},
except that
- * this method can be invoked on an instance of the parent {@code
CodeList} class.
*
* @return all code {@linkplain #values() values} for this code list.
*/
@Override
public SensorType[] family() {
- return values();
+ return values(SensorType.class);
}
/**
- * Returns the sensor type that matches the given string, or returns a
- * new one if none match it. More specifically, this methods returns the
first instance for
- * which <code>{@linkplain #name() name()}.{@linkplain String#equals
equals}(code)</code>
- * returns {@code true}. If no existing instance is found, then a new one
is created for
- * the given name.
+ * Returns the sensor type that matches the given string, or returns a new
one if none match it.
*
* @param code the name of the code to fetch or to create.
* @return a code matching the given name.
*/
public static SensorType valueOf(String code) {
- return valueOf(SensorType.class, code);
+ return valueOf(SensorType.class, code, SensorType::new).get();
}
}
diff --git
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeChildrenTest.java
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeChildrenTest.java
index d80f63a2b2..c776b19885 100644
---
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeChildrenTest.java
+++
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeChildrenTest.java
@@ -183,7 +183,7 @@ public final class TreeNodeChildrenTest extends TestCase {
"Some title",
"First alternate title",
"Some edition",
- "PresentationForm[MAP_DIGITAL]",
+ "PresentationForm.MAP_DIGITAL",
"Some other details"
};
assertEquals(-1, children.titleProperty);
@@ -205,8 +205,8 @@ public final class TreeNodeChildrenTest extends TestCase {
"First alternate title",
"Second alternate title",
"Some edition",
- "PresentationForm[MAP_DIGITAL]",
- "PresentationForm[MAP_HARDCOPY]",
+ "PresentationForm.MAP_DIGITAL",
+ "PresentationForm.MAP_HARDCOPY",
"Some other details"
};
assertEquals(-1, children.titleProperty);
@@ -234,7 +234,7 @@ public final class TreeNodeChildrenTest extends TestCase {
final TreeNodeChildren children = (TreeNodeChildren)
node.getChildren();
final String[] expected = {
// The "Date" node should be omitted because merged with the
parent "Date" node.
- "DateType[CREATION]"
+ "DateType.CREATION"
};
assertEquals(0, children.titleProperty);
assertFalse (children.isEmpty());
@@ -260,9 +260,9 @@ public final class TreeNodeChildrenTest extends TestCase {
"Second alternate title",
"Third alternate title", // After addition
"New edition", // After "addition"
(actually change).
- "PresentationForm[IMAGE_DIGITAL]", // After addition
- "PresentationForm[MAP_DIGITAL]",
- "PresentationForm[MAP_HARDCOPY]",
+ "PresentationForm.IMAGE_DIGITAL", // After addition
+ "PresentationForm.MAP_DIGITAL",
+ "PresentationForm.MAP_HARDCOPY",
"Some other details"
};
toAdd.setValue(TableColumn.IDENTIFIER, "edition");
@@ -358,8 +358,8 @@ public final class TreeNodeChildrenTest extends TestCase {
null, // edition date
null, // identifiers (collection)
null, // cited responsibly parties (collection)
- "PresentationForm[MAP_DIGITAL]",
- "PresentationForm[MAP_HARDCOPY]",
+ "PresentationForm.MAP_DIGITAL",
+ "PresentationForm.MAP_HARDCOPY",
null, // series
"Some other details",
// null, // collective title -- deprecated as
of ISO 19115:2014.
diff --git
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/citation/DefaultContactTest.java
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/citation/DefaultContactTest.java
index 1eb3c935fd..a358d615bd 100644
---
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/citation/DefaultContactTest.java
+++
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/citation/DefaultContactTest.java
@@ -101,7 +101,7 @@ public final class DefaultContactTest extends TestCase
implements Filter {
*/
assertSame(tel2, contact.getPhone()); // Shall ignore the
TelephoneType.SMS.
assertEquals("IgnoredPropertyAssociatedTo_1", resourceKey);
- assertArrayEquals(new String[] {"TelephoneType[SMS]"}, parameters);
+ assertArrayEquals(new String[] {"TelephoneType.SMS"}, parameters);
verifyLegacyLists(tels);
}
diff --git
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
index c34a78c918..59fdada10d 100644
---
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
@@ -147,18 +147,18 @@ public final class TypesTest extends TestCase {
*/
@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_CENTER,
Types.forCodeName(PixelInCell.class, "cell centre", false));
- assertSame(PixelInCell.CELL_CENTER,
Types.forCodeName(PixelInCell.class, "cellCentre", 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));
}
/**
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
index 203f5e1cc8..0dbbbd3bc0 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
@@ -1029,7 +1029,7 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
}
unit = defaultUnit;
}
- AxisDirection direction = Types.forCodeName(AxisDirection.class,
orientation.keyword, true);
+ AxisDirection direction = Types.forCodeName(AxisDirection.class,
orientation.keyword, AxisDirection::valueOf);
final Element meridian = element.pullElement(OPTIONAL,
WKTKeywords.Meridian);
if (meridian != null) {
double angle = meridian.pullDouble("meridian");
@@ -1576,7 +1576,7 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
}
final String name = element.pullString("name");
final PixelInCell pixelInCell = Types.forCodeName(PixelInCell.class,
- element.pullVoidElement("pixelInCell").keyword, true);
+ element.pullVoidElement("pixelInCell").keyword,
PixelInCell::valueOf);
final DatumFactory datumFactory = factories.getDatumFactory();
try {
return datumFactory.createImageDatum(parseAnchorAndClose(element,
name), pixelInCell);
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DirectionAlongMeridian.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DirectionAlongMeridian.java
index fab0c65e7f..640690fe0b 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DirectionAlongMeridian.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DirectionAlongMeridian.java
@@ -169,7 +169,7 @@ final class DirectionAlongMeridian extends
FormattableObject implements Comparab
final String name = toString();
direction = AxisDirections.valueOf(name);
if (direction == null) {
- direction = Types.forCodeName(AxisDirection.class, name, true);
+ direction = Types.forCodeName(AxisDirection.class, name,
AxisDirection::valueOf);
}
}
return direction;
diff --git
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
index 93c573a7db..b7720a9ffd 100644
---
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
@@ -23,9 +23,10 @@ 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:
@@ -187,7 +188,7 @@ public final class VerticalDatumTypes implements
Predicate<CodeList<?>> {
for (int i=0; i<name.length();) {
final int c = name.codePointAt(i);
if (Character.isLetter(c)) {
- return CodeList.valueOf(VerticalDatumType.class, new
VerticalDatumTypes(name), null);
+ return CodeLists.find(VerticalDatumType.class, new
VerticalDatumTypes(name));
}
i += Character.charCount(c);
}
diff --git
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/MetadataReader.java
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/MetadataReader.java
index 232450ba92..ae58750f54 100644
---
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/MetadataReader.java
+++
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/MetadataReader.java
@@ -69,6 +69,7 @@ import org.apache.sis.system.Configuration;
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.iso.Types;
import org.apache.sis.util.privy.CollectionsExt;
+import org.apache.sis.util.privy.CodeLists;
import org.apache.sis.util.privy.Strings;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.measure.Units;
@@ -269,11 +270,12 @@ split: while ((start =
CharSequences.skipLeadingWhitespaces(value, start, lengt
* In the latter case, this method emits a warning.
*/
private <T extends Enum<T>> T forEnumName(final Class<T> enumType, final
String name) {
- final T code = Types.forEnumName(enumType, name);
- if (code == null && name != null) {
- warning(Errors.Keys.UnknownEnumValue_2, enumType, name, null);
+ try {
+ return CodeLists.forEnumName(enumType, name);
+ } catch (IllegalArgumentException e) {
+ warning(Errors.Keys.UnknownEnumValue_2, enumType, name, e);
+ return null;
}
- return code;
}
/**
@@ -281,7 +283,7 @@ split: while ((start =
CharSequences.skipLeadingWhitespaces(value, start, lengt
* In the latter case, this method emits a warning.
*/
private <T extends CodeList<T>> T forCodeName(final Class<T> codeType,
final String name) {
- final T code = Types.forCodeName(codeType, name, false);
+ final T code = Types.forCodeName(codeType, name, null);
if (code == null && name != null) {
/*
* CodeLists are not enums, but using the error message for enums
is not completly wrong since
diff --git
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Axis.java
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Axis.java
index ebd2896d8a..0f1799a1e0 100644
---
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Axis.java
+++
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Axis.java
@@ -206,7 +206,7 @@ public final class Axis extends NamedElement {
* give precedence to choices #2 and #3 in that order. Choice #1 is
not considered authoritative
* because it applies (in principle) only to vertical axis.
*/
- AxisDirection dir = Types.forCodeName(AxisDirection.class, direction,
false);
+ AxisDirection dir = Types.forCodeName(AxisDirection.class, direction,
null);
AxisDirection check = AxisDirections.fromAbbreviation(abbreviation);
final boolean isSigned = (dir != null); // Whether `dir` takes in
account the direction of positive values.
boolean isConsistent = true;
@@ -277,7 +277,7 @@ public final class Axis extends NamedElement {
case 'N': return AxisDirection.NORTH;
}
}
- return Types.forCodeName(AxisDirection.class, direction,
false);
+ return Types.forCodeName(AxisDirection.class, direction, null);
}
}
return null;
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/StringConverter.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/StringConverter.java
index e15ea38b65..73f5eae546 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/StringConverter.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/StringConverter.java
@@ -354,7 +354,7 @@ abstract class StringConverter<T> extends
SystemConverter<String, T> {
/** Converts the given string to the target type of this converter. */
@Override T doConvert(final String source) {
- final T code = CodeLists.forName(targetClass, source, false);
+ final T code = CodeLists.forCodeName(targetClass, source);
if (code == null) {
throw new
UnconvertibleObjectException(formatErrorMessage(source));
}
@@ -388,11 +388,11 @@ abstract class StringConverter<T> extends
SystemConverter<String, T> {
/** Converts the given string to the target type of this converter. */
@Override T doConvert(final String source) {
- final T code = CodeLists.forName(targetClass, source);
- if (code == null) {
- throw new
UnconvertibleObjectException(formatErrorMessage(source));
+ try {
+ return CodeLists.forEnumName(targetClass, source);
+ } catch (IllegalArgumentException e) {
+ throw new
UnconvertibleObjectException(formatErrorMessage(source), e);
}
- return code;
}
/** Invoked by the constructor for creating the inverse converter. */
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/CodeListSet.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/CodeListSet.java
index 95c9c2ff39..92e0be6861 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/CodeListSet.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/CodeListSet.java
@@ -24,7 +24,6 @@ import java.util.NoSuchElementException;
import java.io.Serializable;
import java.lang.reflect.Modifier;
import org.opengis.util.CodeList;
-import org.apache.sis.util.privy.CodeLists;
import org.apache.sis.util.privy.CheckedArrayList;
import org.apache.sis.util.resources.Errors;
@@ -136,7 +135,7 @@ public class CodeListSet<E extends CodeList<E>> extends
AbstractSet<E>
public CodeListSet(final Class<E> elementType, final boolean fill) throws
IllegalArgumentException {
this(elementType);
if (fill) {
- codes = POOL.unique(CodeLists.values(elementType));
+ codes = POOL.unique(CodeList.values(elementType));
int n = codes.length;
if (n < Long.SIZE) {
values = (1L << n) - 1;
@@ -167,7 +166,7 @@ public class CodeListSet<E extends CodeList<E>> extends
AbstractSet<E>
final E valueOf(final int ordinal) {
E[] array = codes;
if (array == null || ordinal >= array.length) {
- codes = array = POOL.unique(CodeLists.values(elementType));
+ codes = array = POOL.unique(CodeList.values(elementType));
}
return array[ordinal];
}
diff --git
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
index 87c842258e..323e2cb003 100644
---
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,15 +16,16 @@
*/
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 java.util.function.Predicate;
import org.opengis.util.ControlledVocabulary;
@@ -42,6 +43,8 @@ public final class CodeLists implements
Predicate<CodeList<?>> {
/**
* Creates a new filter for the specified code name.
+ *
+ * @param codename the name to compare during filtering operation.
*/
private CodeLists(final String codename) {
this.codename = codename;
@@ -64,52 +67,28 @@ public final class CodeLists implements
Predicate<CodeList<?>> {
/**
* Returns {@code true} if the given names matches the name we are looking
for.
- * This is defined in a separated method in order to ensure that all code
paths
- * use the same criterion.
*/
private static boolean accept(final String candidate, final String
codename) {
return CharSequences.equalsFiltered(candidate, codename,
Filter.LETTERS_AND_DIGITS, true);
}
/**
- * Returns the code of the given type that matches the given name,
- * or optionally returns a new one if none match the name.
- *
- * @param <T> 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}.
- * @param canCreate {@code true} if this method is allowed to create new
code.
- * @return a code matching the given name, or {@code null}.
- *
- * @see org.apache.sis.util.iso.Types#forCodeName(Class, String, boolean)
- */
- public static <T extends CodeList<T>> T forName(final Class<T> codeType,
String name, final boolean canCreate) {
- name = Strings.trimOrNull(name);
- if (name == null) {
- return null;
- }
- return CodeList.valueOf(codeType, new CodeLists(name), canCreate ?
name : null);
- }
-
- /**
- * Returns the enumeration value of the given type that matches the given
name, or {@code null} if none.
+ * Returns the enumeration value of the given type that matches the given
name.
*
* @param <T> the compile-time type given as the {@code enumType}
parameter.
* @param enumType the type of enumeration.
* @param name the name of the enumeration value to obtain, or
{@code null}.
- * @return a value matching the given name, or {@code null}.
+ * @return a value matching the given name, or {@code null} if the given
name was null or blank.
+ * @throws IllegalArgumentException if no enumeration value matches the
given name.
*
* @see org.apache.sis.util.iso.Types#forEnumName(Class, String)
*/
- public static <T extends Enum<T>> T forName(final Class<T> enumType,
String name) {
+ public static <T extends Enum<T>> T forEnumName(final Class<T> enumType,
String name) {
name = Strings.trimOrNull(name);
if (name != null) try {
return Enum.valueOf(enumType, name);
} catch (IllegalArgumentException e) {
final T[] values = enumType.getEnumConstants();
- if (values == null) {
- throw e;
- }
if (values instanceof ControlledVocabulary[]) {
for (final ControlledVocabulary code :
(ControlledVocabulary[]) values) {
for (final String candidate : code.names()) {
@@ -118,31 +97,67 @@ public final class CodeLists implements
Predicate<CodeList<?>> {
}
}
}
- } else {
+ } else if (values != null) {
for (final Enum<?> code : values) {
if (accept(code.name(), name)) {
return enumType.cast(code);
}
}
}
+ throw e;
}
return null;
}
/**
- * Returns all known values for the given type of code list or enumeration.
+ * Returns the code of the given type that matches the given name.
*
- * @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 <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;
+ }
+
+ /**
+ * Returns the code of the given type that matches the filter.
*
- * @see org.apache.sis.util.iso.Types#getCodeValues(Class)
+ * @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.
*/
- @SuppressWarnings("unchecked")
- public static <T extends ControlledVocabulary> T[] values(final Class<T>
codeType) {
- Object values;
- try {
- values = codeType.getMethod("values", (Class<?>[])
null).invoke(null, (Object[]) null);
+ 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;
+ }
+
+ /**
+ * 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));
} catch (InvocationTargetException e) {
final Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
@@ -151,10 +166,13 @@ public final class CodeLists implements
Predicate<CodeList<?>> {
if (cause instanceof Error) {
throw (Error) cause;
}
+ // `CodeList.valueOf(String)` methods are not expected to throw
checked exceptions.
throw new UndeclaredThrowableException(cause);
- } catch (NoSuchMethodException | IllegalAccessException e) {
- values = Array.newInstance(codeType, 0);
+ } catch (IllegalAccessException e) {
+ throw (InaccessibleObjectException) new
InaccessibleObjectException(e.getMessage()).initCause(e);
+ } catch (NoSuchMethodException | NullPointerException e) {
+ throw new
IllegalArgumentException(Errors.format(Errors.Keys.ElementNotFound_1, name), e);
}
- return (T[]) values;
+ return code;
}
}
diff --git
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/CodeListSetTest.java
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/CodeListSetTest.java
index 5a20b6cfb3..6f449ee356 100644
---
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/CodeListSetTest.java
+++
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/CodeListSetTest.java
@@ -213,7 +213,7 @@ public final class CodeListSetTest extends TestCase {
public void testFill() {
final CodeListSet<AxisDirection> c = new
CodeListSet<>(AxisDirection.class, true);
assertTrue(c.size() >= 32, "Expect at least 32 elements as of GeoAPI
3.0.");
- assertTrue(c.toString().startsWith("[AxisDirection[OTHER],
AxisDirection[NORTH], "));
+ assertTrue(c.toString().startsWith("[AxisDirection.OTHER,
AxisDirection.NORTH, "));
/*
* Testing the full array would be too long and may change in future
GeoAPI version
* anyway. Actually the main interest of this test is to ensure that
the toString()
diff --git
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/LargeCodeList.java
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/LargeCodeList.java
index 0eb8405306..17954d32fb 100644
---
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/LargeCodeList.java
+++
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/LargeCodeList.java
@@ -16,8 +16,6 @@
*/
package org.apache.sis.util.collection;
-import java.util.List;
-import java.util.ArrayList;
import org.opengis.util.CodeList;
// Test dependencies
@@ -31,27 +29,23 @@ import static org.junit.jupiter.api.Assertions.*;
* @author Martin Desruisseaux (Geomatys)
*/
@SuppressWarnings("serial")
-public final class LargeCodeList extends CodeList<LargeCodeList> {
- /**
- * List of all enumerations of this type.
- */
- private static final List<LargeCodeList> VALUES = new ArrayList<>(100);
-
+public final class LargeCodeList extends CodeList<LargeCodeList> {
/**
* Creates 100 code list elements.
+ * We need to construct values with {@code valueOf(String)} instead of the
constructor
+ * because this package is not exported to GeoAPI. See {@link CodeList}
class javadoc.
*/
static {
- for (int i=0; i<100; i++) {
- assertEquals(i, new LargeCodeList(i).ordinal());
+ for (int i=0; i<80; i++) {
+ assertEquals(i, valueOf("LC#" + i).ordinal());
}
}
/**
- * Constructs an element. The new element is automatically
- * added to the list to be returned by {@link #values}.
+ * Constructs an element.
*/
- private LargeCodeList(final int i) {
- super("LC#" + i, VALUES);
+ private LargeCodeList(String name) {
+ super(name);
}
/**
@@ -60,9 +54,7 @@ public final class LargeCodeList extends
CodeList<LargeCodeList> {
* @return the list of codes declared in the current JVM.
*/
public static LargeCodeList[] values() {
- synchronized (VALUES) {
- return VALUES.toArray(LargeCodeList[]::new);
- }
+ return values(LargeCodeList.class);
}
/**
@@ -76,13 +68,12 @@ public final class LargeCodeList extends
CodeList<LargeCodeList> {
}
/**
- * Returns the axis code that matches the given string,
- * or returns a new one if none match it.
+ * Returns the code that matches the given string, or returns a new one if
none match it.
*
* @param code the name of the code list element to fetch or to create.
* @return a code list element matching the given name.
*/
public static LargeCodeList valueOf(final String code) {
- return valueOf(LargeCodeList.class, code);
+ return valueOf(LargeCodeList.class, code, LargeCodeList::new).get();
}
}
diff --git a/geoapi/snapshot b/geoapi/snapshot
index c786929b56..b2c419d046 160000
--- a/geoapi/snapshot
+++ b/geoapi/snapshot
@@ -1 +1 @@
-Subproject commit c786929b56e8f8aed8eceb0144734bbd7ac28ea6
+Subproject commit b2c419d046315ef580eb8eb746bb20361df79f7d
diff --git
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
index 61a92019c1..0f7237ee39 100644
---
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
@@ -16,8 +16,6 @@
*/
package org.apache.sis.gui.referencing;
-import java.util.List;
-import java.util.ArrayList;
import org.opengis.util.CodeList;
import org.opengis.util.FactoryException;
import org.opengis.geometry.DirectPosition;
@@ -45,17 +43,11 @@ import static org.apache.sis.gui.internal.LogHandler.LOGGER;
* The point of interest is typically determined by mouse location.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.4
+ * @version 1.5
* @since 1.1
*/
@SuppressWarnings("serial") // We do not guarantee serialization
compatibility.
public abstract class PositionableProjection extends
CodeList<PositionableProjection> {
- /**
- * List of all enumerations of this type.
- * Must be declared before any enum declaration.
- */
- private static final List<PositionableProjection> VALUES = new
ArrayList<>(1);
-
/**
* Provides <cite>Orthographic</cite> projection centered on a point of
interest.
*/
@@ -134,22 +126,18 @@ public abstract class PositionableProjection extends
CodeList<PositionableProjec
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) {
- super(name, VALUES);
+ PositionableProjection(final String name, final short nameKey) {
+ super(name);
this.nameKey = nameKey;
}
@@ -159,9 +147,7 @@ public abstract class PositionableProjection extends
CodeList<PositionableProjec
* @return the list of codes declared in the current JVM.
*/
public static PositionableProjection[] values() {
- synchronized (VALUES) {
- return VALUES.toArray(PositionableProjection[]::new);
- }
+ return values(PositionableProjection.class);
}
/**
@@ -176,16 +162,6 @@ public abstract class PositionableProjection extends
CodeList<PositionableProjec
return values();
}
- /**
- * Disables the search for UML identifiers because we do not export this
package to GeoAPI.
- *
- * @return {@code null}.
- */
- @Override
- public String identifier() {
- return null;
- }
-
/**
* Returns a name for this enumeration which can be used in a user
interface.
*
diff --git
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/package-info.java
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/package-info.java
index b403df321f..eda03d9a69 100644
---
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/package-info.java
+++
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/package-info.java
@@ -20,7 +20,7 @@
*
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.4
+ * @version 1.5
* @since 1.1
*/
package org.apache.sis.gui.referencing;