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

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

commit 00c9d831d07cbe1f6e91ced45e7eb42ea626e509
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Fri Jan 7 19:59:31 2022 +0100

    Add a test for TIFF `GEO_METADATA` tag. The test using temporal extent 
forces us to add a minimalist "sis-temporal" implementation.
---
 .../apache/sis/test/integration/MetadataTest.java  |  22 +--
 .../org/apache/sis/test/integration/Metadata.xml   |   4 +-
 .../sis/internal/temporal/DefaultInstant.java      |  66 +++++++++
 .../sis/internal/temporal/DefaultPeriod.java       |  72 ++++++++++
 .../internal/temporal/DefaultPeriodDuration.java   |  88 ++++++++++++
 .../internal/temporal/DefaultTemporalFactory.java  | 150 +++++++++++++++++++++
 .../apache/sis/internal/temporal/Primitive.java    |  75 +++++++++++
 .../apache/sis/internal/temporal/package-info.java |  27 ++++
 .../sis/internal/util/TemporalUtilities.java       |   6 +-
 .../sis/util/collection/TreeTableFormat.java       |   2 +-
 .../sis/storage/landsat/MetadataReaderTest.java    |   4 +-
 storage/sis-geotiff/pom.xml                        |   5 +
 .../apache/sis/storage/geotiff/XMLMetadata.java    |   3 +-
 .../sis/storage/geotiff/XMLMetadataTest.java       |  57 +++++++-
 .../sis/storage/netcdf/MetadataReaderTest.java     |   4 +-
 .../sis/internal/storage/MetadataBuilder.java      |   2 +-
 16 files changed, 556 insertions(+), 31 deletions(-)

diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/test/integration/MetadataTest.java
 
b/core/sis-referencing/src/test/java/org/apache/sis/test/integration/MetadataTest.java
index f90ecb0..4656208 100644
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/test/integration/MetadataTest.java
+++ 
b/core/sis-referencing/src/test/java/org/apache/sis/test/integration/MetadataTest.java
@@ -86,7 +86,7 @@ import static org.apache.sis.test.Assert.*;
  *
  * @author  Guilhem Legal (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.2
  *
  * @see org.apache.sis.metadata.iso.DefaultMetadataTest
  *
@@ -114,26 +114,10 @@ public final strictfp class MetadataTest extends TestCase 
{
      */
     @After
     public void assertNoUnexpectedLog() {
-        loggings.skipNextLogIfContains("sis-temporal");
         loggings.assertNoUnexpectedLog();
     }
 
     /**
-     * Sets the temporal extent. The current implementation does nothing, 
because {@code sis-metadata} does not have
-     * any dependency to {@code sis-temporal}. However a future version or an 
other module may implement this method.
-     *
-     * @param  extent     the extent to set.
-     * @param  startTime  the start time in the {@code "yyyy-MM-dd"} format.
-     * @param  endTime    the end time in the {@code "yyyy-MM-dd"} format.
-     */
-    protected void setTemporalBounds(final DefaultTemporalExtent extent, final 
String startTime, final String endTime) {
-        /*
-         * Note: if this MetadataTest class is made final and this method 
removed,
-         *       then testUnmarshalling() can be simplified.
-         */
-    }
-
-    /**
      * Programmatically creates the metadata to marshal, or to compare against 
the unmarshalled metadata.
      *
      * @return the hard-coded representation of {@value #XML_FILE} content.
@@ -272,7 +256,7 @@ public final strictfp class MetadataTest extends TestCase {
                         nameAndIdentifier("D28", "Depth below D28", "CRS for 
testing purpose"), datum, cs);
 
                 final DefaultTemporalExtent temporal = new 
DefaultTemporalExtent();
-                setTemporalBounds(temporal, "1990-06-05", "1990-07-02");
+                temporal.setBounds(TestUtilities.date("1990-06-05 00:00:00"), 
TestUtilities.date("1990-07-02 00:00:00"));
                 identification.setExtents(singleton(new DefaultExtent(
                         null,
                         new DefaultGeographicBoundingBox(1.1666, 1.1667, 36.4, 
36.6),
@@ -432,7 +416,7 @@ public final strictfp class MetadataTest extends TestCase {
     private static void replace(final StringBuffer buffer, final String 
toSearch, final String replaceBy) {
         final int i = buffer.indexOf(toSearch);
         assertTrue("String to replace not found.", i >= 0);
-        buffer.replace(i, i+toSearch.length(), replaceBy);
+        buffer.replace(i, i + toSearch.length(), replaceBy);
     }
 
     /**
diff --git 
a/core/sis-referencing/src/test/resources/org/apache/sis/test/integration/Metadata.xml
 
b/core/sis-referencing/src/test/resources/org/apache/sis/test/integration/Metadata.xml
index c4c29fb..f6bbea0 100644
--- 
a/core/sis-referencing/src/test/resources/org/apache/sis/test/integration/Metadata.xml
+++ 
b/core/sis-referencing/src/test/resources/org/apache/sis/test/integration/Metadata.xml
@@ -390,8 +390,8 @@
             <gmd:EX_TemporalExtent>
               <gmd:extent>
                 <gml:TimePeriod gml:id="extent">
-                  <gml:beginPosition>1990-06-05</gml:beginPosition>
-                  <gml:endPosition>1990-07-02</gml:endPosition>
+                  <gml:beginPosition>1990-06-05T00:00:00Z</gml:beginPosition>
+                  <gml:endPosition>1990-07-02T00:00:00Z</gml:endPosition>
                 </gml:TimePeriod>
               </gmd:extent>
             </gmd:EX_TemporalExtent>
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultInstant.java
 
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultInstant.java
new file mode 100644
index 0000000..b312280
--- /dev/null
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultInstant.java
@@ -0,0 +1,66 @@
+/*
+ * 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.internal.temporal;
+
+import java.util.Date;
+import org.opengis.temporal.Instant;
+import org.opengis.temporal.TemporalPosition;
+
+
+/**
+ * Default implementation of GeoAPI instant. This is a temporary class;
+ * GeoAPI temporal interfaces are expected to change a lot in a future 
revision.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since   1.2
+ * @module
+ */
+final class DefaultInstant extends Primitive implements Instant {
+    /** The date in milliseconds since epoch. */
+    private final long millis;
+
+    /** Creates a new instant for the given date. */
+    DefaultInstant(final Date time) {
+        millis = time.getTime();
+    }
+
+    /** Returns the date used for describing temporal position. */
+    @Override public Date getDate() {
+        return new Date(millis);
+    }
+
+    /** Association to a temporal reference system. */
+    @Override public TemporalPosition getTemporalPosition() {
+        throw DefaultTemporalFactory.unsupported();
+    }
+
+    /** String representation in ISO format. */
+    @Override public String toString() {
+        return java.time.Instant.ofEpochMilli(millis).toString();
+    }
+
+    /** Hash code value of the time position. */
+    @Override public int hashCode() {
+        return Long.hashCode(millis) ^ 57;
+    }
+
+    /** Compares with given object for equality. */
+    @Override public boolean equals(final Object obj) {
+        return (obj instanceof DefaultInstant) && ((DefaultInstant) 
obj).millis == millis;
+    }
+}
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultPeriod.java
 
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultPeriod.java
new file mode 100644
index 0000000..353d644
--- /dev/null
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultPeriod.java
@@ -0,0 +1,72 @@
+/*
+ * 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.internal.temporal;
+
+import java.util.Objects;
+import org.opengis.temporal.Instant;
+import org.opengis.temporal.Period;
+
+
+/**
+ * Default implementation of GeoAPI period. This is a temporary class;
+ * GeoAPI temporal interfaces are expected to change a lot in a future 
revision.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since   1.2
+ * @module
+ */
+final class DefaultPeriod extends Primitive implements Period {
+    /** Bounds making this period. */
+    private final Instant beginning, ending;
+
+    /** Creates a new period with the given bounds. */
+    DefaultPeriod(final Instant beginning, final Instant ending) {
+        this.beginning = beginning;
+        this.ending    = ending;
+    }
+
+    /** The beginning instant at which this period starts. */
+    @Override public Instant getBeginning() {
+        return beginning;
+    }
+
+    /** The ending instant at which this period ends. */
+    @Override public Instant getEnding() {
+        return ending;
+    }
+
+    /** String representation. */
+    @Override public String toString() {
+        return "[" + beginning + " … " + ending + ']';
+    }
+
+    /** Hash code value of the time position. */
+    @Override public int hashCode() {
+        return Objects.hash(beginning, ending);
+    }
+
+    /** Compares with given object for equality. */
+    @Override public boolean equals(final Object obj) {
+        if (obj instanceof DefaultPeriod) {
+            DefaultPeriod other = (DefaultPeriod) obj;
+            return Objects.equals(other.beginning, beginning) &&
+                   Objects.equals(other.ending, ending);
+        }
+        return false;
+    }
+}
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultPeriodDuration.java
 
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultPeriodDuration.java
new file mode 100644
index 0000000..052a12c
--- /dev/null
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultPeriodDuration.java
@@ -0,0 +1,88 @@
+/*
+ * 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.internal.temporal;
+
+import java.util.Objects;
+import org.opengis.temporal.PeriodDuration;
+import org.opengis.util.InternationalString;
+
+
+/**
+ * Default implementation of GeoAPI period duration. This is a temporary class;
+ * GeoAPI temporal interfaces are expected to change a lot in a future 
revision.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since   1.2
+ * @module
+ */
+final class DefaultPeriodDuration implements PeriodDuration {
+    /** Components of this period duration. */
+    private final InternationalString years, months, week, days, hours, 
minutes, seconds;
+
+    /**
+     * Creates a new duration.
+     */
+    DefaultPeriodDuration(
+            final InternationalString years, final InternationalString months,
+            final InternationalString week,  final InternationalString days,
+            final InternationalString hours, final InternationalString 
minutes, final InternationalString seconds)
+    {
+        this.years   = years;
+        this.months  = months;
+        this.week    = week;
+        this.days    = days;
+        this.hours   = hours;
+        this.minutes = minutes;
+        this.seconds = seconds;
+    }
+
+
+    @Override public InternationalString getDesignator()    {return null;}
+    @Override public InternationalString getYears()         {return years;}
+    @Override public InternationalString getMonths()        {return months;}
+    @Override public InternationalString getDays()          {return days;}
+    @Override public InternationalString getTimeIndicator() {return null;}
+    @Override public InternationalString getHours()         {return hours;}
+    @Override public InternationalString getMinutes()       {return minutes;}
+    @Override public InternationalString getSeconds()       {return seconds;}
+
+    /** String representation. */
+    @Override public String toString() {
+        return "PeriodDuration[" + years + '-' + months + '-' + days + ' ' + 
hours + ':' + minutes + ':' + seconds + ']';
+    }
+
+    /** Hash code value of the time position. */
+    @Override public int hashCode() {
+        return Objects.hash(years, months, week, days, hours, minutes, 
seconds);
+    }
+
+    /** Compares with given object for equality. */
+    @Override public boolean equals(final Object obj) {
+        if (obj instanceof DefaultPeriodDuration) {
+            DefaultPeriodDuration other = (DefaultPeriodDuration) obj;
+            return Objects.equals(other.years,   years) &&
+                   Objects.equals(other.months,  months) &&
+                   Objects.equals(other.week,    week) &&
+                   Objects.equals(other.days,    days) &&
+                   Objects.equals(other.hours,   hours) &&
+                   Objects.equals(other.minutes, minutes) &&
+                   Objects.equals(other.seconds, seconds);
+        }
+        return false;
+    }
+}
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultTemporalFactory.java
 
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultTemporalFactory.java
new file mode 100644
index 0000000..d040aea
--- /dev/null
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultTemporalFactory.java
@@ -0,0 +1,150 @@
+/*
+ * 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.internal.temporal;
+
+import java.util.Date;
+import java.util.Collection;
+import javax.measure.Unit;
+import javax.measure.quantity.Time;
+import org.opengis.temporal.*;
+import org.opengis.metadata.Identifier;
+import org.opengis.metadata.extent.Extent;
+import org.opengis.util.InternationalString;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * Default implementation of temporal object factory. This is a temporary 
class;
+ * GeoAPI temporal interfaces are expected to change a lot in a future 
revision.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since   1.2
+ * @module
+ */
+public final class DefaultTemporalFactory implements TemporalFactory {
+    /** The unique instance of this factory. */
+    public static final TemporalFactory INSTANCE = new 
DefaultTemporalFactory();
+
+    /** Creates the singleton instance. */
+    private DefaultTemporalFactory() {
+    }
+
+    /** Creates an {@link Instant} for the given date. */
+    @Override public Instant createInstant(Date date) {
+        return new DefaultInstant(date);
+    }
+
+    /** Creates a period for the two given instants. */
+    @Override public Period createPeriod(Instant begin, Instant end) {
+        return new DefaultPeriod(begin, end);
+    }
+
+    /** Creates a period duration. */
+    @Override public PeriodDuration createPeriodDuration(
+            InternationalString years, InternationalString months,
+            InternationalString week,  InternationalString days,
+            InternationalString hours, InternationalString minutes, 
InternationalString seconds)
+    {
+        return new DefaultPeriodDuration(years, months, week, days, hours, 
minutes, seconds);
+    }
+
+    /** Returns the exception to be thrown by all unsupported methods. */
+    static UnsupportedOperationException unsupported() {
+        return new 
UnsupportedOperationException(Errors.format(Errors.Keys.MissingRequiredModule_1,
 "sis-temporal"));
+    }
+
+    /** Unsupported. */
+    @Override public Calendar createCalendar(Identifier name, Extent 
domainOfValidity) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public Calendar createCalendar(Identifier name, Extent 
domainOfValidity, Collection<CalendarEra> referenceFrame, Clock timeBasis) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public CalendarDate createCalendarDate(TemporalReferenceSystem 
frame, IndeterminateValue indeterminatePosition, InternationalString 
calendarEraName, int[] calendarDate) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public CalendarEra createCalendarEra(InternationalString name, 
InternationalString referenceEvent, CalendarDate referenceDate, JulianDate 
julianReference, Period epochOfUse) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public Clock createClock(Identifier name, Extent 
domainOfValidity, InternationalString referenceEvent, ClockTime referenceTime, 
ClockTime utcReference) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public ClockTime createClockTime(TemporalReferenceSystem frame, 
IndeterminateValue indeterminatePosition, Number[] clockTime) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public DateAndTime createDateAndTime(TemporalReferenceSystem 
frame, IndeterminateValue indeterminatePosition, InternationalString 
calendarEraName, int[] calendarDate, Number[] clockTime) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public IntervalLength createIntervalLenght(Unit unit, int radix, 
int factor, int value) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public JulianDate createJulianDate(TemporalReferenceSystem 
frame, IndeterminateValue indeterminatePosition, Number coordinateValue) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public OrdinalEra createOrdinalEra(InternationalString name, 
Date beginning, Date end, Collection<OrdinalEra> member) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public OrdinalPosition 
createOrdinalPosition(TemporalReferenceSystem frame, IndeterminateValue 
indeterminatePosition, OrdinalEra ordinalPosition) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public OrdinalReferenceSystem 
createOrdinalReferenceSystem(Identifier name, Extent domainOfValidity, 
Collection<OrdinalEra> ordinalEraSequence) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public TemporalCoordinate 
createTemporalCoordinate(TemporalReferenceSystem frame, IndeterminateValue 
indeterminatePosition, Number coordinateValue) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public TemporalCoordinateSystem 
createTemporalCoordinateSystem(Identifier name, Extent domainOfValidity, Date 
origin, Unit<Time> interval) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public TemporalPosition 
createTemporalPosition(TemporalReferenceSystem frame, IndeterminateValue 
indeterminatePosition) {
+        throw unsupported();
+    }
+
+    /** Unsupported. */
+    @Override public TemporalReferenceSystem 
createTemporalReferenceSystem(Identifier name, Extent domainOfValidity) {
+        throw unsupported();
+    }
+}
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/Primitive.java
 
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/Primitive.java
new file mode 100644
index 0000000..412c704
--- /dev/null
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/Primitive.java
@@ -0,0 +1,75 @@
+/*
+ * 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.internal.temporal;
+
+import org.opengis.metadata.Identifier;
+import org.opengis.temporal.Duration;
+import org.opengis.temporal.RelativePosition;
+import org.opengis.temporal.TemporalGeometricPrimitive;
+import org.opengis.temporal.TemporalPrimitive;
+
+
+/**
+ * Base implementation of GeoAPI temporal primitives. This is a temporary 
class;
+ * GeoAPI temporal interfaces are expected to change a lot in a future 
revision.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since   1.2
+ * @module
+ */
+class Primitive implements TemporalGeometricPrimitive, Identifier {
+    /**
+     * For sub-class constructors.
+     */
+    Primitive() {
+    }
+
+    /**
+     * The primary name by which this object is identified.
+     * This field is inherited from ISO 19111 {@code IdentifiedObject} and is 
in principle mandatory.
+     */
+    @Override
+    public final Identifier getName() {
+        return this;
+    }
+
+    /**
+     * Returns the string representation as the code for this object. This is 
not a correct identifier code,
+     * but we use that as a trick for forcing {@link 
org.apache.sis.util.collection.TreeTableFormat} to show
+     * the temporal value, because the formatter handles {@link 
org.opengis.referencing.IdentifiedObject} in
+     * a special way.
+     */
+    @Override public final String getCode() {
+        return toString();
+    }
+
+    /** position of this primitive relative to another primitive. */
+    @Override public final RelativePosition relativePosition(TemporalPrimitive 
other) {
+        throw DefaultTemporalFactory.unsupported();
+    }
+
+    /** Absolute value of the difference between temporal positions. */
+    @Override public final Duration distance(TemporalGeometricPrimitive other) 
{
+        throw DefaultTemporalFactory.unsupported();
+    }
+
+    /** Duration of this temporal geometric primitive. */
+    @Override public final Duration length() {
+        return null;    // Do not throw an exception here; this is invoked by 
reflection.
+    }
+}
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/package-info.java
 
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/package-info.java
new file mode 100644
index 0000000..b5039c3
--- /dev/null
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/**
+ * Place-holder for a future "sis-temporal" module. This is a temporary 
package;
+ * the temporal GeoAPI interfaces are expected to change a lot in a future 
revision.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since   1.2
+ * @module
+ */
+package org.apache.sis.internal.temporal;
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java
 
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java
index 46b554d..c4f697d 100644
--- 
a/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java
@@ -22,8 +22,8 @@ import org.opengis.temporal.Period;
 import org.opengis.temporal.TemporalFactory;
 import org.opengis.temporal.TemporalPrimitive;
 import org.apache.sis.util.Static;
-import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.internal.temporal.DefaultTemporalFactory;
 
 
 /**
@@ -32,7 +32,7 @@ import org.apache.sis.internal.system.DefaultFactories;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Guilhem Legal (Geomatys)
- * @version 1.0
+ * @version 1.2
  * @since   0.3
  * @module
  */
@@ -63,7 +63,7 @@ public final class TemporalUtilities extends Static {
         if (factory != null) {
             return factory;
         }
-        throw new 
UnsupportedOperationException(Errors.format(Errors.Keys.MissingRequiredModule_1,
 "sis-temporal"));
+        return DefaultTemporalFactory.INSTANCE;
     }
 
     /**
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
 
b/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
index fbddfdb..24dbef0 100644
--- 
a/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
@@ -898,7 +898,7 @@ public class TreeTableFormat extends 
TabularFormat<TreeTable> {
 
     /**
      * Creates a new format to use for parsing and formatting values of the 
given type.
-     * This method is invoked by the first time that a format is needed for 
the given type.
+     * This method is invoked the first time that a format is needed for the 
given type.
      * Subclasses can override this method if they want to configure the way 
dates, numbers
      * or other objects are formatted.
      * See {@linkplain org.apache.sis.io.CompoundFormat#createFormat(Class) 
parent class documentation}
diff --git 
a/storage/sis-earth-observation/src/test/java/org/apache/sis/storage/landsat/MetadataReaderTest.java
 
b/storage/sis-earth-observation/src/test/java/org/apache/sis/storage/landsat/MetadataReaderTest.java
index 32df297..fe050d3 100644
--- 
a/storage/sis-earth-observation/src/test/java/org/apache/sis/storage/landsat/MetadataReaderTest.java
+++ 
b/storage/sis-earth-observation/src/test/java/org/apache/sis/storage/landsat/MetadataReaderTest.java
@@ -29,6 +29,7 @@ import org.opengis.metadata.content.CoverageContentType;
 import org.opengis.metadata.content.TransferFunctionType;
 import org.opengis.metadata.identification.Progress;
 import org.opengis.metadata.identification.TopicCategory;
+import org.opengis.metadata.extent.TemporalExtent;
 import org.opengis.metadata.maintenance.ScopeCode;
 import org.opengis.metadata.spatial.DimensionNameType;
 import org.opengis.util.FactoryException;
@@ -46,7 +47,7 @@ import static org.apache.sis.test.TestUtilities.date;
  *
  * @author  Thi Phuong Hao Nguyen (VNSC)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
  * @since   0.8
  * @module
  */
@@ -85,6 +86,7 @@ public class MetadataReaderTest extends TestCase {
         final ContentVerifier verifier = new ContentVerifier();
         verifier.addPropertyToIgnore(Metadata.class, "metadataStandard");      
     // Because hard-coded in SIS.
         verifier.addPropertyToIgnore(Metadata.class, "referenceSystemInfo");   
     // Very verbose and depends on EPSG connection.
+        verifier.addPropertyToIgnore(TemporalExtent.class, "extent");          
     // Because currently time-zone sensitive.
         verifier.addMetadataToVerify(actual);
         verifier.addExpectedValues(
             "defaultLocale+otherLocale[0]",                                    
                      "en",
diff --git a/storage/sis-geotiff/pom.xml b/storage/sis-geotiff/pom.xml
index 5cb9a4f..9453689 100644
--- a/storage/sis-geotiff/pom.xml
+++ b/storage/sis-geotiff/pom.xml
@@ -132,6 +132,11 @@
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.glassfish.jaxb</groupId>
+      <artifactId>jaxb-runtime</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
 </project>
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/XMLMetadata.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/XMLMetadata.java
index 299163f..5c94f25 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/XMLMetadata.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/XMLMetadata.java
@@ -356,7 +356,8 @@ final class XMLMetadata implements Filter {
                  * The `mergeMetadata` method applies heuristic rules for 
adding components.
                  */
                 metadata.mergeMetadata(XML.unmarshal(new StAXSource(reader),
-                        Collections.singletonMap(XML.WARNING_FILTER, this)), 
listeners.getLocale());
+                        Collections.singletonMap(XML.WARNING_FILTER, this)),
+                        (listeners != null) ? listeners.getLocale() : null);
             }
             reader.close();     // No need to close the underlying input 
stream.
         }
diff --git 
a/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/XMLMetadataTest.java
 
b/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/XMLMetadataTest.java
index cb21eba..8d86a63 100644
--- 
a/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/XMLMetadataTest.java
+++ 
b/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/XMLMetadataTest.java
@@ -18,8 +18,10 @@ package org.apache.sis.storage.geotiff;
 
 import org.apache.sis.metadata.iso.DefaultMetadata;
 import org.apache.sis.internal.storage.MetadataBuilder;
+import org.apache.sis.internal.xml.LegacyNamespaces;
 import org.apache.sis.util.collection.DefaultTreeTable;
 import org.apache.sis.util.collection.TableColumn;
+import org.apache.sis.xml.Namespaces;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
@@ -36,7 +38,7 @@ import static org.apache.sis.test.Assert.*;
  */
 public final strictfp class XMLMetadataTest extends TestCase {
     /**
-     * A GDAL metadata.
+     * A GDAL metadata. The format is specific to the GDAL project.
      */
     private static final String GDAL_METADATA =
             "<GDALMetadata>\n" +
@@ -48,6 +50,34 @@ public final strictfp class XMLMetadataTest extends TestCase 
{
             "</GDALMetadata>\n";
 
     /**
+     * A DGIWG metadata in ISO 19115 format.
+     */
+    private static final String GEO_METADATA =
+            "<gmd:MD_Metadata\n" +
+            "  xmlns:gmd = \"" + LegacyNamespaces.GMD + "\"\n"  +
+            "  xmlns:gml = \"" + Namespaces.GML + "\"\n>" +
+            "  <gmd:identificationInfo>\n" +
+            "    <gmd:MD_DataIdentification>\n" +
+            "      <gmd:extent>\n" +
+            "        <gmd:EX_Extent>\n" +
+            "          <gmd:temporalElement>\n" +
+            "            <gmd:EX_TemporalExtent>\n" +
+            "              <gmd:extent>\n" +
+            "                <gml:TimePeriod>\n" +
+            "                  <gml:description>Acquisition 
period</gml:description>\n" +
+            "                  
<gml:beginPosition>2018-02-28T03:04:00</gml:beginPosition>\n" +
+            "                  
<gml:endPosition>2018-02-28T04:48:00</gml:endPosition>\n" +
+            "                </gml:TimePeriod>\n" +
+            "              </gmd:extent>\n" +
+            "            </gmd:EX_TemporalExtent>\n" +
+            "          </gmd:temporalElement>\n" +
+            "        </gmd:EX_Extent>\n" +
+            "      </gmd:extent>\n" +
+            "    </gmd:MD_DataIdentification>\n" +
+            "  </gmd:identificationInfo>\n" +
+            "</gmd:MD_Metadata>\n";
+
+    /**
      * Tests parsing of GDAL metadata and formatting as a tree table.
      * THe XML document is like below:
      *
@@ -100,7 +130,30 @@ public final strictfp class XMLMetadataTest extends 
TestCase {
         assertMultilinesEquals(
                 "Metadata\n" +
                 "  └─Identification info\n" +
-                "      └─Citation………………… My image\n",
+                "      ├─Citation……………………………… My image\n" +
+                "      └─Extent\n" +
+                "          └─Temporal element\n" +
+                "              └─Extent……………… [2018-02-28T04:48:00Z … 
2018-02-28T03:04:00Z]\n",
+                metadata.toString());
+    }
+
+    /**
+     * Tests parsing DGIWG metadata and conversion to ISO 19115 metadata.
+     *
+     * @throws Exception if an error occurred during XML parsing.
+     */
+    @Test
+    public void testGeoMetadata() throws Exception {
+        XMLMetadata xml = new XMLMetadata(GEO_METADATA, false);
+        MetadataBuilder builder = new MetadataBuilder();
+        xml.appendTo(builder);
+        DefaultMetadata metadata = builder.build(false);
+        assertMultilinesEquals(
+                "Metadata\n" +
+                "  └─Identification info\n" +
+                "      └─Extent\n" +
+                "          └─Temporal element\n" +
+                "              └─Extent……………… [2018-02-28T02:04:00Z … 
2018-02-28T03:48:00Z]\n",
                 metadata.toString());
     }
 }
diff --git 
a/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java
 
b/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java
index 333f091..339ae0d 100644
--- 
a/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java
+++ 
b/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java
@@ -20,6 +20,7 @@ import java.io.IOException;
 import org.opengis.metadata.Metadata;
 import org.opengis.metadata.citation.Role;
 import org.opengis.metadata.citation.DateType;
+import org.opengis.metadata.extent.TemporalExtent;
 import org.opengis.metadata.identification.KeywordType;
 import org.opengis.metadata.content.TransferFunctionType;
 import org.opengis.metadata.spatial.SpatialRepresentationType;
@@ -44,7 +45,7 @@ import static org.apache.sis.test.TestUtilities.date;
  * for reading netCDF attributes.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
  * @since   0.3
  * @module
  */
@@ -104,6 +105,7 @@ public final strictfp class MetadataReaderTest extends 
TestCase {
         final ContentVerifier verifier = new ContentVerifier();
         verifier.addPropertyToIgnore(Metadata.class, "metadataStandard");
         verifier.addPropertyToIgnore(Metadata.class, "referenceSystemInfo");
+        verifier.addPropertyToIgnore(TemporalExtent.class, "extent");
         verifier.addMetadataToVerify(actual);
         verifier.addExpectedValues(
             // Hard-coded
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
index 5e498bf..a10e3e3 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
@@ -3209,7 +3209,7 @@ parse:      for (int i = 0; i < length;) {
      * but some types of metadata components are accepted as well.
      *
      * @param  source  the source metadata to merge. Will never be modified.
-     * @param  locale  the locale to use for error message in exceptions.
+     * @param  locale  the locale to use for error message in exceptions, or 
{@code null} for the default locale.
      * @return {@code true} if the given source has been merged,
      *         or {@code false} if its type is not managed by this builder.
      * @throws RuntimeException if the merge failed (may be {@link 
IllegalArgumentException},

Reply via email to