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

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

commit 2aea6e15e370793cbfd5324e4c2f68b6e18a9a42
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Thu Jun 13 16:58:14 2024 +0200

    Add a `GeneralDuration.parse(CharSequence)` method.
    Add more factory methods.
---
 .../org/apache/sis/temporal/DefaultInstant.java    |  5 ++
 .../org/apache/sis/temporal/GeneralDuration.java   | 48 +++++++++++++++----
 .../org/apache/sis/temporal/TemporalUtilities.java | 55 +++++++++++++++++++++-
 .../apache/sis/temporal/GeneralDurationTest.java   | 52 ++++++++++++++++++++
 4 files changed, 151 insertions(+), 9 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/DefaultInstant.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/DefaultInstant.java
index ea299d910d..52064eeb7a 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/DefaultInstant.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/DefaultInstant.java
@@ -50,6 +50,11 @@ final class DefaultInstant implements Instant, Serializable {
      */
     private static final long serialVersionUID = 3898772638524283287L;
 
+    /**
+     * The constant for the "unknown" instant.
+     */
+    static final DefaultInstant UNKNOWN = new DefaultInstant(null, 
IndeterminateValue.UNKNOWN);
+
     /**
      * The temporal position as a date, time or date/time.
      * May be {@code null} if {@link #indeterminate} is non-null and not 
"before" or "after".
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/GeneralDuration.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/GeneralDuration.java
index f7b08ab404..882721f7e5 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/GeneralDuration.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/GeneralDuration.java
@@ -45,15 +45,12 @@ import org.opengis.temporal.IndeterminatePositionException;
 
 /**
  * A data type to be used for describing length or distance in the temporal 
dimension.
- * This implementation combine {@link java.time.Period} with {@link 
java.time.Duration}
- * for situation where both of them are needed together (which is not 
recommended).
- *
- * This class also contains a {@code distance(…)} method for computing the 
distance between two ISO 19108 instants.
- * This is defined here for reducing class loading in the common case where 
{@code distance(…)} is not invoked.
+ * This implementation combines {@link java.time.Period} with {@link 
java.time.Duration}
+ * for situations where both of them are needed together (which is not 
recommended).
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
-final class GeneralDuration implements TemporalAmount, Serializable {
+public final class GeneralDuration implements TemporalAmount, Serializable {
     /**
      * For cross-version compatibility.
      */
@@ -63,13 +60,13 @@ final class GeneralDuration implements TemporalAmount, 
Serializable {
      * The period in numbers of years, months and days.
      * Shall be non-null and non-zero.
      */
-    private final Period period;
+    public final Period period;
 
     /**
      * The time part of the period in numbers of hours, minutes and seconds.
      * Shall be non-null, non-zero and less than one day.
      */
-    private final Duration time;
+    public final Duration time;
 
     /**
      * Creates a new instance with the given parts.
@@ -83,6 +80,41 @@ final class GeneralDuration implements TemporalAmount, 
Serializable {
         this.time   = time;
     }
 
+    /**
+     * Parses a temporal amount which may contain a period and a duration part.
+     * This method returns a {@link Period} or {@link Duration} if those 
objects
+     * are sufficient, or an instance of {@code GeneralDuration} is last 
resort.
+     *
+     * @param  text  the text to parse.
+     * @return the parsed period and/or duration.
+     * @throws DateTimeParseException if the given text cannot be parsed.
+     *
+     * @see Period#parse(CharSequence)
+     * @see Duration#parse(CharSequence)
+     */
+    public static TemporalAmount parse(final CharSequence text) {
+        char previousLetter = 0;
+        final int length = text.length();
+        for (int i=0; i<length; i++) {
+            char c = text.charAt(i);
+            if (c >= 'a' && c <= 'z') c -= 'a' - 'A';       // Quick upper 
case, ASCII characters only.
+            if (c >= 'A' && c <= 'Z') {
+                if (c == 'T') {
+                    if (previousLetter == 'P') {
+                        return Duration.parse(text);
+                    }
+                    var period   = Period.parse(text.subSequence(0, i));
+                    var duration = Duration.parse(new StringBuilder(length - i 
+ 1).append('P').append(text, i, length));
+                    if (duration.isZero()) return period;
+                    if  (period.isZero())  return duration;
+                    return new GeneralDuration(period, duration);
+                }
+                previousLetter = c;
+            }
+        }
+        return Period.parse(text);
+    }
+
     /**
      * Returns the temporal position of the given instant if that position is 
determinate or is "now".
      * Otherwise, throws an exception. If the position is "now", then this 
method returns {@code null}
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/TemporalUtilities.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/TemporalUtilities.java
index 250fa7ef56..e96f6c2220 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/TemporalUtilities.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/TemporalUtilities.java
@@ -19,6 +19,8 @@ package org.apache.sis.temporal;
 import java.util.Date;
 import java.time.temporal.Temporal;
 import org.opengis.temporal.TemporalPrimitive;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
 import org.opengis.temporal.IndeterminateValue;
@@ -46,7 +48,42 @@ public final class TemporalUtilities {
      * @return the instant, or an unknown instant if the given time was null.
      */
     public static Instant createInstant(final Temporal time) {
-        return new DefaultInstant(time, (time != null) ? null : 
IndeterminateValue.UNKNOWN);
+        return (time == null) ? DefaultInstant.UNKNOWN : new 
DefaultInstant(time, null);
+    }
+
+    /**
+     * Creates an instant for the given Java temporal instant associated to 
the indeterminate value.
+     * This is used for creating "before" or "after" instant.
+     *
+     * @param  time   the date for which to create instant.
+     * @param  value  the indeterminate value.
+     * @return the instant.
+     */
+    public static Instant createInstant(final Temporal time, final 
IndeterminateValue value) {
+        ArgumentChecks.ensureNonNull("value", value);
+        if (value == IndeterminateValue.UNKNOWN) {
+            return DefaultInstant.UNKNOWN;
+        }
+        if (value == IndeterminateValue.BEFORE || value == 
IndeterminateValue.AFTER) {
+            ArgumentChecks.ensureNonNull("time", time);
+        }
+        return new DefaultInstant(time, value);
+    }
+
+    /**
+     * Creates an instant for the given indeterminate value.
+     * The given value cannot be "before" or "after".
+     *
+     * @param  value  the indeterminate value.
+     * @return the instant for the given indeterminate value.
+     * @throws IllegalArgumentException if the given value is "before" or 
"after".
+     */
+    public static Instant createInstant(final IndeterminateValue value) {
+        ArgumentChecks.ensureNonNull("value", value);
+        if (value == IndeterminateValue.BEFORE || value == 
IndeterminateValue.AFTER) {
+            throw new 
IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, 
"value", value));
+        }
+        return (value == IndeterminateValue.UNKNOWN) ? DefaultInstant.UNKNOWN 
: new DefaultInstant(null, value);
     }
 
     /**
@@ -63,6 +100,22 @@ public final class TemporalUtilities {
         return new DefaultPeriod(createInstant(beginning), 
createInstant(ending));
     }
 
+    /**
+     * Creates a period for the given begin and end instant.
+     *
+     * @param  beginning  the begin instant (inclusive), or {@code null}.
+     * @param  ending     the end instant (exclusive), or {@code null}.
+     * @return the period, or {@code null} if both arguments are null.
+     */
+    public static Period createPeriod(Instant beginning, Instant ending) {
+        if (beginning == null && ending == null) {
+            return null;
+        }
+        if (beginning == null) beginning = DefaultInstant.UNKNOWN;
+        if    (ending == null)    ending = DefaultInstant.UNKNOWN;
+        return new DefaultPeriod(beginning, ending);
+    }
+
     /**
      * Returns the given value as a temporal position, or {@code null} if not 
available.
      *
diff --git 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/temporal/GeneralDurationTest.java
 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/temporal/GeneralDurationTest.java
new file mode 100644
index 0000000000..a9a18354a4
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/temporal/GeneralDurationTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.temporal;
+
+import java.time.Period;
+import java.time.Duration;
+
+// Test dependencies
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+import org.apache.sis.test.TestCase;
+
+
+/**
+ * Tests the {@link GeneralDuration} class.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+public final class GeneralDurationTest extends TestCase {
+    /**
+     * Creates a new test case.
+     */
+    public GeneralDurationTest() {
+    }
+
+    /**
+     * Tests {@link GeneralDuration#parse(CharSequence)}.
+     */
+    @Test
+    public void testParse() {
+        assertEquals(Period.of(2, 3, 4),    GeneralDuration.parse("P2Y3M4D"));
+        assertEquals(Duration.ofHours(100), GeneralDuration.parse("PT100H"));
+        assertEquals(Duration.ofHours(200), GeneralDuration.parse("pt200H"));
+        var r = assertInstanceOf(GeneralDuration.class, 
GeneralDuration.parse("P2Y3M4DT10H"));
+        assertEquals(Period.of(2, 3, 4),    r.period);
+        assertEquals(Duration.ofHours(10),  r.time);
+    }
+}

Reply via email to