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 2448d007cd Add converters between strings to various kinds of 
`java.time` objects. This is needed by `SQLStore` when a column is mapped to 
e.g. `LocalDate`.
2448d007cd is described below

commit 2448d007cd4d2ec3505abdf18510c230c20af37a
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Tue Oct 22 17:53:50 2024 +0200

    Add converters between strings to various kinds of `java.time` objects.
    This is needed by `SQLStore` when a column is mapped to e.g. `LocalDate`.
---
 .../org/apache/sis/feature/FeatureOperations.java  |  7 +-
 .../apache/sis/feature/StringJoinOperation.java    | 14 +++-
 .../org/apache/sis/metadata/sql/privy/Syntax.java  |  2 +-
 .../apache/sis/storage/sql/feature/Analyzer.java   |  2 +-
 .../apache/sis/storage/sql/feature/Database.java   |  1 -
 .../sis/storage/DataStoreContentException.java     |  2 +-
 .../src/org.apache.sis.util/main/module-info.java  | 11 +++
 .../apache/sis/converter/ConverterRegistry.java    | 12 ++--
 .../org/apache/sis/converter/DateConverter.java    | 51 ++++++++-----
 .../org/apache/sis/converter/InstantConverter.java | 83 ++++++++++++++++++++++
 .../org/apache/sis/converter/StringConverter.java  | 82 +++++++++++++++++++++
 .../main/org/apache/sis/math/FunctionProperty.java |  4 +-
 12 files changed, 234 insertions(+), 37 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureOperations.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureOperations.java
index f88ee704ac..4ab41f8df7 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureOperations.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureOperations.java
@@ -24,7 +24,6 @@ import org.opengis.util.InternationalString;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.Static;
-import org.apache.sis.util.UnconvertibleObjectException;
 import org.apache.sis.util.collection.WeakHashSet;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.privy.Strings;
@@ -199,17 +198,15 @@ public final class FeatureOperations extends Static {
      * @param  suffix            characters to use at the end of the 
concatenated string, or {@code null} if none.
      * @param  singleAttributes  identification of the single attributes (or 
operations producing attributes) to concatenate.
      * @return an operation which concatenates the string representations of 
all referenced single property values.
-     * @throws UnconvertibleObjectException if at least one of the given 
{@code singleAttributes} uses a
-     *         {@linkplain DefaultAttributeType#getValueClass() value class} 
which is not convertible from a {@link String}.
      * @throws IllegalArgumentException if {@code singleAttributes} is an 
empty sequence, or contains a property which
      *         is neither an {@code AttributeType} or an {@code Operation} 
computing an attribute, or an attribute has
-     *         a {@linkplain DefaultAttributeType#getMaximumOccurs() maximum 
number of occurrences} greater than 1.
+     *         a {@linkplain DefaultAttributeType#getMaximumOccurs() maximum 
number of occurrences} greater than 1, or
+     *         uses a {@linkplain DefaultAttributeType#getValueClass() value 
class} not convertible from a {@link String}.
      *
      * @see <a href="https://en.wikipedia.org/wiki/Compound_key";>Compound key 
on Wikipedia</a>
      */
     public static Operation compound(final Map<String,?> identification, final 
String delimiter,
             final String prefix, final String suffix, final PropertyType... 
singleAttributes)
-            throws UnconvertibleObjectException
     {
         ArgumentChecks.ensureNonEmpty("delimiter", delimiter);
         if (delimiter.indexOf(StringJoinOperation.ESCAPE) >= 0) {
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/StringJoinOperation.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/StringJoinOperation.java
index 7b075094e9..b702dc10e9 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/StringJoinOperation.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/StringJoinOperation.java
@@ -176,12 +176,14 @@ final class StringJoinOperation extends AbstractOperation 
{
      * It is caller's responsibility to ensure that {@code delimiter} and 
{@code singleAttributes} are not null.
      * This private constructor does not verify that condition on the 
assumption that the public API did.
      *
+     * @throws UnconvertibleObjectException if at least one attributes is not 
convertible from a string.
+     * @throws IllegalArgumentException if the operation failed for another 
reason.
+     *
      * @see FeatureOperations#compound(Map, String, String, String, 
PropertyType...)
      */
     @SuppressWarnings({"rawtypes", "unchecked"})                               
         // Generic array creation.
     StringJoinOperation(final Map<String,?> identification, final String 
delimiter,
             final String prefix, final String suffix, final PropertyType[] 
singleAttributes)
-            throws UnconvertibleObjectException
     {
         super(identification);
         attributeNames = new String[singleAttributes.length];
@@ -234,8 +236,14 @@ final class StringJoinOperation extends AbstractOperation {
              * We need only their names and how to convert from String to 
their values.
              */
             attributeNames[i] = name.toString();
-            ObjectConverter<? super String, ?> converter = 
ObjectConverters.find(
-                    String.class, ((AttributeType<?>) 
propertyType).getValueClass());
+            final Class<?> valueClass = ((AttributeType<?>) 
propertyType).getValueClass();
+            ObjectConverter<? super String, ?> converter;
+            try {
+                converter = ObjectConverters.find(String.class, valueClass);
+            } catch (UnconvertibleObjectException e) {
+                throw new 
UnconvertibleObjectException(Resources.forProperties(identification)
+                        .getString(Resources.Keys.IllegalPropertyType_2, name, 
valueClass), e);
+            }
             if (isAssociation) {
                 converter = new ForFeature(converter);
             }
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/Syntax.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/Syntax.java
index ab3e64bac5..ef21416737 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/Syntax.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/Syntax.java
@@ -51,7 +51,7 @@ public class Syntax {
      * The string that can be used to escape wildcard characters.
      * This is the value returned by {@link 
DatabaseMetaData#getSearchStringEscape()}.
      */
-    final String escape;
+    protected final String escape;
 
     /**
      * Creates a new {@code Syntax} initialized from the given database 
metadata.
diff --git 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Analyzer.java
 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Analyzer.java
index e38ddd9b51..645ea0bd47 100644
--- 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Analyzer.java
+++ 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Analyzer.java
@@ -165,7 +165,7 @@ public final class Analyzer {
          * Finds the keyword used for identifying tables and views.
          * Derby, HSQLDB and PostgreSQL uses the "TABLE" type, but H2 uses 
"BASE TABLE".
          */
-        final Set<String> types = new HashSet<>(4);
+        final var types = new HashSet<String>(4);
         try (ResultSet reflect = metadata.getTableTypes()) {
             while (reflect.next()) {
                 final String type = reflect.getString(Reflection.TABLE_TYPE);
diff --git 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Database.java
 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Database.java
index 6d9a1f9593..b09cfed294 100644
--- 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Database.java
+++ 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Database.java
@@ -304,7 +304,6 @@ public class Database<G> extends Syntax  {
      * @return names of the standard tables defined by the spatial schema.
      */
     final Set<String> detectSpatialSchema(final DatabaseMetaData metadata, 
final String[] tableTypes) throws SQLException {
-        final String escape = metadata.getSearchStringEscape();
         /*
          * The following tables are defined by ISO 19125 / OGC Simple feature 
access part 2.
          * Note that the standard specified those names in upper-case letters, 
which is also
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/DataStoreContentException.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/DataStoreContentException.java
index 3528b8ec0d..cec525b44e 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/DataStoreContentException.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/DataStoreContentException.java
@@ -20,7 +20,7 @@ import java.util.Locale;
 
 
 /**
- * Thrown when a store cannot be read because the stream contains invalid data.
+ * Thrown when a store cannot be read because the stream or database contains 
invalid data.
  * It may be for example a logical inconsistency, or a reference not found,
  * or an unsupported file format version, <i>etc.</i>
  *
diff --git a/endorsed/src/org.apache.sis.util/main/module-info.java 
b/endorsed/src/org.apache.sis.util/main/module-info.java
index 040cfe0af5..9264ad41fe 100644
--- a/endorsed/src/org.apache.sis.util/main/module-info.java
+++ b/endorsed/src/org.apache.sis.util/main/module-info.java
@@ -45,6 +45,15 @@ module org.apache.sis.util {
              org.apache.sis.converter.StringConverter.Double,
              org.apache.sis.converter.StringConverter.BigInteger,
              org.apache.sis.converter.StringConverter.BigDecimal,
+             org.apache.sis.converter.StringConverter.Instant,
+             org.apache.sis.converter.StringConverter.ZonedDateTime,
+             org.apache.sis.converter.StringConverter.OffsetDateTime,
+             org.apache.sis.converter.StringConverter.LocalDateTime,
+             org.apache.sis.converter.StringConverter.LocalDate,
+             org.apache.sis.converter.StringConverter.LocalTime,
+             org.apache.sis.converter.StringConverter.Year,
+             org.apache.sis.converter.StringConverter.YearMonth,
+             org.apache.sis.converter.StringConverter.MonthDay,
              org.apache.sis.converter.StringConverter.Boolean,
              org.apache.sis.converter.StringConverter.Locale,
              org.apache.sis.converter.StringConverter.Charset,
@@ -72,6 +81,8 @@ module org.apache.sis.util {
              org.apache.sis.converter.DateConverter.Long,
              org.apache.sis.converter.DateConverter.SQL,
              org.apache.sis.converter.DateConverter.Timestamp,
+             org.apache.sis.converter.DateConverter.Instant,
+             org.apache.sis.converter.InstantConverter.Date,
              org.apache.sis.converter.CollectionConverter.List,
              org.apache.sis.converter.CollectionConverter.Set,
              org.apache.sis.converter.FractionConverter,
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/ConverterRegistry.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/ConverterRegistry.java
index 487fe9ce8b..8fc1453f9e 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/ConverterRegistry.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/ConverterRegistry.java
@@ -245,7 +245,7 @@ public class ConverterRegistry {
          * ConverterRegistry), unwraps it and registers its component 
individually.
          */
         if (converter instanceof FallbackConverter<?,?>) {
-            final FallbackConverter<S,T> fc = (FallbackConverter<S,T>) 
converter;
+            final var fc = (FallbackConverter<S,T>) converter;
             register(fc.primary);
             register(fc.fallback);
             return;
@@ -282,12 +282,14 @@ public class ConverterRegistry {
                      */
                     continue;
                 }
-                if (i.getName().startsWith("java.lang.constant")) {
+                switch (i.getPackageName()) {
                     /*
-                     * The Constable and ConstantDesc interfaces (introduced 
in Java 12)
-                     * are internal mechanic for handling byte codes.
+                     * The Constable and ConstantDesc interfaces (introduced 
in Java 12) are internal mechanic
+                     * for handling byte codes. The temporal interfaces are 
unusual in that users are advised
+                     * to use a specific implementation class instead of the 
interface.
                      */
-                    continue;
+                    case "java.lang.constant":
+                    case "java.time.temporal": continue;
                 }
                 if (Cloneable.class.isAssignableFrom(i)) {
                     /*
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/DateConverter.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/DateConverter.java
index da07589113..a14b1cfe57 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/DateConverter.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/DateConverter.java
@@ -21,18 +21,13 @@ import java.util.Set;
 import java.util.EnumSet;
 import org.apache.sis.util.ObjectConverter;
 import org.apache.sis.math.FunctionProperty;
+import org.apache.sis.util.UnconvertibleObjectException;
 
 
 /**
  * Handles conversions from {@link Date} to various objects.
- *
- * <h2>String representation</h2>
- * There is currently no converter between {@link String} and {@link 
java.util.Date} because the
- * date format is not yet defined (we are considering the ISO format for a 
future SIS version).
- *
- * <h2>Special cases</h2>
- * The converter from dates to timestamps is not injective, because the same 
date could be mapped
- * to many timestamps since timestamps have an additional nanoseconds field.
+ * Note that there is no converter between {@link String} and {@link 
java.util.Date}.
+ * The {@link java.time.Instant} class should be used instead.
  *
  * <h2>Immutability and thread safety</h2>
  * This base class and all inner classes are immutable, and thus inherently 
thread-safe.
@@ -61,12 +56,14 @@ abstract class DateConverter<T> extends 
SystemConverter<Date,T> {
     }
 
     /**
-     * Returns the function properties.
+     * Returns the function properties. The function from {@code Date} 
instances to {@code Timestamp} or
+     * {@code Instant} instances is <em>injective</em> because each instant is 
either unrelated to dates
+     * (if the instant contains a nanosecond field), or is the output of 
exactly one {@code Date} with
+     * nanoseconds assumed to be zero.
      */
     @Override
     public Set<FunctionProperty> properties() {
-        return EnumSet.of(FunctionProperty.SURJECTIVE, 
FunctionProperty.ORDER_PRESERVING,
-                FunctionProperty.INVERTIBLE);
+        return EnumSet.of(FunctionProperty.INJECTIVE, 
FunctionProperty.ORDER_PRESERVING, FunctionProperty.INVERTIBLE);
     }
 
     /**
@@ -171,12 +168,30 @@ abstract class DateConverter<T> extends 
SystemConverter<Date,T> {
         }
     }
 
-    /*
-     * We do not yet provide converter to java.time.Instant. If we do so, we 
need to create an InstantConverter class
-     * doing the inverse conversion.  Reminder: java.sql.Date and 
java.sql.Time are not convertible to Instant (their
-     * Date.toInstant() method throws UnsupportedOperationException), but 
java.sql.Timestamp is.
-     *
-     * If conversion to/from java.time.Instant is added, see if some code can 
be shared with
-     * org.apache.sis.filter.ComparisonFilter.
+    /**
+     * From {@code Date} to {@code Instant}.
      */
+    public static final class Instant extends DateConverter<java.time.Instant> 
{
+        private static final long serialVersionUID = 5727173560137117677L;
+
+        static final Instant INSTANCE = new Instant();      // Invoked by 
ServiceLoader when using module-path.
+        public static Instant provider() {
+            return INSTANCE;
+        }
+
+        public Instant() {                                  // Instantiated by 
ServiceLoader when using class-path.
+            super(java.time.Instant.class);
+            inverse = InstantConverter.Date.INSTANCE;
+        }
+
+        @Override public java.time.Instant apply(final Date source) {
+            if (source != null) try {
+                return source.toInstant();
+            } catch (UnsupportedOperationException e) {
+                // Thrown by `java.sql.Date` and `java.sql.Time`, but not 
`java.sql.Timestamp`.
+                throw new 
UnconvertibleObjectException(formatErrorMessage(source), e);
+            }
+            return null;
+        }
+    }
 }
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/InstantConverter.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/InstantConverter.java
new file mode 100644
index 0000000000..d49b6c9bf0
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/converter/InstantConverter.java
@@ -0,0 +1,83 @@
+/*
+ * 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.converter;
+
+import java.util.Set;
+import java.util.EnumSet;
+import java.time.Instant;
+import org.apache.sis.util.ObjectConverter;
+import org.apache.sis.math.FunctionProperty;
+
+
+/**
+ * Handles conversions from {@link Instant} to various objects.
+ *
+ * <h2>Immutability and thread safety</h2>
+ * This base class and all inner classes are immutable, and thus inherently 
thread-safe.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ *
+ * @param <T>  the base type of converted objects.
+ */
+abstract class InstantConverter<T> extends SystemConverter<Instant,T> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -7219681557586687605L;
+
+    /**
+     * Creates a converter for the given target type.
+     * Subclasses must initialize {@link #inverse}.
+     */
+    InstantConverter(final Class<T> targetClass) {
+        super(Instant.class, targetClass);
+    }
+
+    /**
+     * Returns the function properties. The function is <em>surjective</em> 
because any {@code Date} instances
+     * can be created from one or many {@code Instant} instances. The same 
date may be created from many instants
+     * because the nanosecond field is dropped.
+     */
+    @Override
+    public Set<FunctionProperty> properties() {
+        return EnumSet.of(FunctionProperty.SURJECTIVE, 
FunctionProperty.ORDER_PRESERVING, FunctionProperty.INVERTIBLE);
+    }
+
+    /**
+     * From {@code Instant} to {@code Date}.
+     */
+    public static final class Date extends InstantConverter<java.util.Date> {
+        private static final long serialVersionUID = -9192665378798185400L;
+
+        static final Date INSTANCE = new Date();        // Invoked by 
ServiceLoader when using module-path.
+        public static Date provider() {
+            return INSTANCE;
+        }
+
+        public Date() {                                 // Instantiated by 
ServiceLoader when using class-path.
+            super(java.util.Date.class);
+        }
+
+        @Override public ObjectConverter<java.util.Date, Instant> inverse() {
+            return DateConverter.Instant.INSTANCE;
+        }
+
+        @Override public java.util.Date apply(final Instant source) {
+            return (source != null) ? java.util.Date.from(source) : 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 73f5eae546..98227e9dfc 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
@@ -19,6 +19,7 @@ package org.apache.sis.converter;
 import java.util.Set;
 import java.util.EnumSet;
 import java.util.IllformedLocaleException;
+import java.time.format.DateTimeParseException;
 import java.nio.charset.UnsupportedCharsetException;
 import java.net.URISyntaxException;
 import java.net.MalformedURLException;
@@ -232,6 +233,87 @@ abstract class StringConverter<T> extends 
SystemConverter<String, T> {
         }
     }
 
+    public static final class Instant extends 
StringConverter<java.time.Instant> {
+        private static final long serialVersionUID = -786622578610861924L;
+        public Instant() {super(java.time.Instant.class);}
+
+        @Override java.time.Instant doConvert(String source) throws 
DateTimeParseException {
+            return java.time.Instant.parse(source);
+        }
+    }
+
+    public static final class ZonedDateTime extends 
StringConverter<java.time.ZonedDateTime> {
+        private static final long serialVersionUID = 4547600422615778462L;
+        public ZonedDateTime() {super(java.time.ZonedDateTime.class);}
+
+        @Override java.time.ZonedDateTime doConvert(String source) throws 
DateTimeParseException {
+            return java.time.ZonedDateTime.parse(source);
+        }
+    }
+
+    public static final class OffsetDateTime extends 
StringConverter<java.time.OffsetDateTime> {
+        private static final long serialVersionUID = 6438936715171368273L;
+        public OffsetDateTime() {super(java.time.OffsetDateTime.class);}
+
+        @Override java.time.OffsetDateTime doConvert(String source) throws 
DateTimeParseException {
+            return java.time.OffsetDateTime.parse(source);
+        }
+    }
+
+    public static final class LocalDateTime extends 
StringConverter<java.time.LocalDateTime> {
+        private static final long serialVersionUID = 4020225109842204445L;
+        public LocalDateTime() {super(java.time.LocalDateTime.class);}
+
+        @Override java.time.LocalDateTime doConvert(String source) throws 
DateTimeParseException {
+            return java.time.LocalDateTime.parse(source);
+        }
+    }
+
+    public static final class LocalDate extends 
StringConverter<java.time.LocalDate> {
+        private static final long serialVersionUID = -2160961842632015681L;
+        public LocalDate() {super(java.time.LocalDate.class);}
+
+        @Override java.time.LocalDate doConvert(String source) throws 
DateTimeParseException {
+            return java.time.LocalDate.parse(source);
+        }
+    }
+
+    public static final class LocalTime extends 
StringConverter<java.time.LocalTime> {
+        private static final long serialVersionUID = -4872647331214579728L;
+        public LocalTime() {super(java.time.LocalTime.class);}
+
+        @Override java.time.LocalTime doConvert(String source) throws 
DateTimeParseException {
+            return java.time.LocalTime.parse(source);
+        }
+    }
+
+    public static final class Year extends StringConverter<java.time.Year> {
+        private static final long serialVersionUID = 9014595771888427112L;
+        public Year() {super(java.time.Year.class);}
+
+        @Override java.time.Year doConvert(String source) throws 
DateTimeParseException {
+            return java.time.Year.parse(source);
+        }
+    }
+
+    public static final class YearMonth extends 
StringConverter<java.time.YearMonth> {
+        private static final long serialVersionUID = -8552019996811990307L;
+        public YearMonth() {super(java.time.YearMonth.class);}
+
+        @Override java.time.YearMonth doConvert(String source) throws 
DateTimeParseException {
+            return java.time.YearMonth.parse(source);
+        }
+    }
+
+    public static final class MonthDay extends 
StringConverter<java.time.MonthDay> {
+        private static final long serialVersionUID = 7647193120429326557L;
+        public MonthDay() {super(java.time.MonthDay.class);}
+
+        @Override java.time.MonthDay doConvert(String source) throws 
DateTimeParseException {
+            return java.time.MonthDay.parse(source);
+        }
+    }
+
     public static final class Boolean extends 
StringConverter<java.lang.Boolean> {
         private static final long serialVersionUID = 4689076223535035309L;
         public Boolean() {super(java.lang.Boolean.class);}                     
     // Instantiated by ServiceLoader.
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/FunctionProperty.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/FunctionProperty.java
index 262780a669..f86a62cb1b 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/FunctionProperty.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/FunctionProperty.java
@@ -80,7 +80,7 @@ public enum FunctionProperty {
     /**
      * A function is <i>injective</i> if each value of <var>T</var> is either 
unrelated
      * to <var>S</var>, or is the output of exactly one value of <var>S</var>.
-     * For example an {@link org.apache.sis.util.ObjectConverter} doing 
conversions from {@link Integer}
+     * For example, an {@link org.apache.sis.util.ObjectConverter} doing 
conversions from {@link Integer}
      * to {@link String} is an injective function, because no pair of integers 
can produce the same string.
      *
      * <p>A function which is both injective and {@linkplain #SURJECTIVE 
surjective} is a
@@ -95,7 +95,7 @@ public enum FunctionProperty {
     /**
      * A function is <i>surjective</i> if any value of <var>T</var> can be 
created
      * from one or many values of <var>S</var>.
-     * For example an {@link org.apache.sis.util.ObjectConverter} doing 
conversions from {@link String}
+     * For example, an {@link org.apache.sis.util.ObjectConverter} doing 
conversions from {@link String}
      * to {@link Integer} is a surjective function, because there is always at 
least one string for each integer value.
      * Note that such function cannot be injective since many different 
strings can represent the same integer value.
      *

Reply via email to