This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/sis.git
commit 6e63051a6fef034289bf1a562837084075d4f802 Merge: c1009799e7 51124b9153 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Sat Jun 1 00:11:11 2024 +0200 Merge branch 'geoapi-3.1' .../org/apache/sis/filter/ComparisonFilter.java | 4 + .../apache/sis/filter/DefaultFilterFactory.java | 55 +- .../main/org/apache/sis/filter/TemporalFilter.java | 811 ++++----------------- .../org/apache/sis/filter/TemporalOperation.java | 762 +++++++++++++++++++ .../main/org/apache/sis/filter/TimeMethods.java | 393 ++++++++++ .../geoapi/filter/TemporalOperatorName.java | 4 + .../org/apache/sis/filter/TemporalFilterTest.java | 23 +- .../org.apache.sis.metadata/main/module-info.java | 1 + .../sis/metadata/iso/acquisition/DefaultEvent.java | 9 +- .../iso/acquisition/DefaultRequestedDate.java | 36 +- .../iso/acquisition/DefaultRequirement.java | 23 +- .../sis/metadata/iso/citation/DefaultCitation.java | 23 +- .../metadata/iso/citation/DefaultCitationDate.java | 11 +- .../distribution/DefaultStandardOrderProcess.java | 20 +- .../metadata/iso/extent/DefaultTemporalExtent.java | 40 +- .../apache/sis/metadata/iso/extent/Extents.java | 95 ++- .../metadata/iso/identification/DefaultUsage.java | 23 +- .../sis/metadata/privy/ImplementationHelper.java | 23 - .../apache/sis/pending/temporal/DefaultPeriod.java | 12 +- .../sis/pending/temporal/TemporalUtilities.java | 39 +- .../org/apache/sis/xml/bind/gco/GO_DateTime.java | 3 +- .../org/apache/sis/xml/bind/gml/TM_Primitive.java | 43 +- .../apache/sis/xml/bind/gml/TemporalAdapter.java | 4 +- .../org/apache/sis/xml/bind/gml/TimeInstant.java | 20 +- .../apache/sis/xml/bind/gml/TimePeriodBound.java | 6 +- .../org/apache/sis/xml/privy/XmlUtilities.java | 52 +- .../sis/metadata/iso/extent/DefaultExtentTest.java | 15 +- .../metadata/privy/ImplementationHelperTest.java | 19 - .../org/apache/sis/metadata/xml/2007/Extent.xml | 2 - .../org/apache/sis/metadata/xml/2016/Extent.xml | 2 - .../apache/sis/xml/bind/gml/TimePeriodTest.java | 19 +- .../org/apache/sis/xml/privy/XmlUtilitiesTest.java | 12 +- .../test/org/apache/sis/xml/test/TestCase.java | 33 - .../sis/referencing/gazetteer/LocationFormat.java | 7 +- .../main/org/apache/sis/io/wkt/Formatter.java | 12 +- .../apache/sis/io/wkt/GeodeticObjectParser.java | 3 +- .../sis/referencing/datum/AbstractDatum.java | 3 +- .../referencing/datum/DefaultGeodeticDatum.java | 4 +- .../org/apache/sis/referencing/internal/Epoch.java | 6 +- .../sis/referencing/privy/ExtentSelector.java | 101 +-- .../referencing/AbstractReferenceSystemTest.java | 9 +- .../datum/DefaultGeodeticDatumTest.java | 3 +- .../sis/referencing/privy/ExtentSelectorTest.java | 5 +- .../apache/sis/test/integration/MetadataTest.java | 29 +- .../sis/test/integration/MetadataVerticalTest.java | 10 +- .../storage/geotiff/reader/XMLMetadataTest.java | 2 +- .../apache/sis/storage/base/MetadataBuilder.java | 3 +- .../apache/sis/pending/geoapi/temporal/Period.java | 6 +- .../main/org/apache/sis/util/Classes.java | 20 +- .../org/apache/sis/util/privy/TemporalDate.java | 113 ++- .../main/org/apache/sis/util/resources/Errors.java | 5 + .../apache/sis/util/resources/Errors.properties | 1 + .../apache/sis/util/resources/Errors_fr.properties | 1 + .../org/apache/sis/measure/RangeFormatTest.java | 8 +- .../test/org/apache/sis/test/TestUtilities.java | 4 +- .../sis/gui/metadata/IdentificationInfo.java | 9 +- 56 files changed, 1841 insertions(+), 1160 deletions(-) diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DefaultFilterFactory.java index 80379a395d,c020754996..03264883a2 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DefaultFilterFactory.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DefaultFilterFactory.java @@@ -32,12 -32,16 +32,13 @@@ import org.apache.sis.filter.sqlmm.Regi import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.iso.AbstractFactory; import org.apache.sis.util.resources.Errors; + import org.apache.sis.util.privy.Strings; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import java.util.Iterator; -import java.time.Instant; -import org.opengis.filter.*; -import org.opengis.feature.Feature; -import org.opengis.filter.capability.AvailableFunction; -import org.opengis.filter.capability.FilterCapabilities; -import org.apache.sis.util.privy.AbstractMap; +// Specific to the main branch: +import org.apache.sis.feature.AbstractFeature; +import org.apache.sis.pending.geoapi.filter.MatchAction; +import org.apache.sis.pending.geoapi.filter.SpatialOperatorName; +import org.apache.sis.pending.geoapi.filter.DistanceOperatorName; /** @@@ -128,14 -128,13 +132,13 @@@ public abstract class DefaultFilterFact } /** - * Returns a factory operating on {@link Feature} instances. + * Returns a factory operating on {@link AbstractFeature} instances. * The {@linkplain GeometryLibrary geometry library} will be the system default. + * The temporal objects can be {@link java.util.Date} or implementations of {@link java.time.temporal.Temporal}. * - * @return factory operating on {@link Feature} instances. + * @return factory operating on {@link AbstractFeature} instances. - * - * @todo The type of temporal objects is not yet determined. */ - public static FilterFactory<Feature, Object, Object> forFeatures() { + public static DefaultFilterFactory<AbstractFeature, Object, Object> forFeatures() { return Features.DEFAULT; } @@@ -643,11 -725,14 +646,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "After" operator between the two temporal values. - * - * @see TemporalOperatorName#AFTER */ - @Override - public TemporalOperator<R> after(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> after(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.After<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.After::new, time1, time2); } /** @@@ -656,11 -741,14 +659,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "Before" operator between the two temporal values. - * - * @see TemporalOperatorName#BEFORE */ - @Override - public TemporalOperator<R> before(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> before(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.Before<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.Before::new, time1, time2); } /** @@@ -669,11 -757,14 +672,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "Begins" operator between the two temporal values. - * - * @see TemporalOperatorName#BEGINS */ - @Override - public TemporalOperator<R> begins(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> begins(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.Begins<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.Begins::new, time1, time2); } /** @@@ -682,11 -773,14 +685,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "BegunBy" operator between the two temporal values. - * - * @see TemporalOperatorName#BEGUN_BY */ - @Override - public TemporalOperator<R> begunBy(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> begunBy(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.BegunBy<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.BegunBy::new, time1, time2); } /** @@@ -695,11 -789,14 +698,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "TContains" operator between the two temporal values. - * - * @see TemporalOperatorName#CONTAINS */ - @Override - public TemporalOperator<R> tcontains(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> tcontains(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.Contains<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.Contains::new, time1, time2); } /** @@@ -708,11 -805,14 +711,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "During" operator between the two temporal values. - * - * @see TemporalOperatorName#DURING */ - @Override - public TemporalOperator<R> during(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> during(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.During<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.During::new, time1, time2); } /** @@@ -721,11 -821,14 +724,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "TEquals" operator between the two temporal values. - * - * @see TemporalOperatorName#EQUALS */ - @Override - public TemporalOperator<R> tequals(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> tequals(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.Equals<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.Equals::new, time1, time2); } /** @@@ -734,11 -837,14 +737,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "TOverlaps" operator between the two temporal values. - * - * @see TemporalOperatorName#OVERLAPS */ - @Override - public TemporalOperator<R> toverlaps(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> toverlaps(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.Overlaps<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.Overlaps::new, time1, time2); } /** @@@ -747,11 -853,14 +750,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "Meets" operator between the two temporal values. - * - * @see TemporalOperatorName#MEETS */ - @Override - public TemporalOperator<R> meets(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> meets(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.Meets<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.Meets::new, time1, time2); } /** @@@ -760,11 -869,14 +763,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "Ends" operator between the two temporal values. - * - * @see TemporalOperatorName#ENDS */ - @Override - public TemporalOperator<R> ends(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> ends(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.Ends<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.Ends::new, time1, time2); } /** @@@ -773,11 -885,14 +776,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "OverlappedBy" operator between the two temporal values. - * - * @see TemporalOperatorName#OVERLAPPED_BY */ - @Override - public TemporalOperator<R> overlappedBy(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> overlappedBy(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.OverlappedBy<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.OverlappedBy::new, time1, time2); } /** @@@ -786,11 -901,14 +789,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "MetBy" operator between the two temporal values. - * - * @see TemporalOperatorName#MET_BY */ - @Override - public TemporalOperator<R> metBy(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> metBy(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.MetBy<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.MetBy::new, time1, time2); } /** @@@ -799,11 -917,14 +802,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "EndedBy" operator between the two temporal values. - * - * @see TemporalOperatorName#ENDED_BY */ - @Override - public TemporalOperator<R> endedBy(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> endedBy(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.EndedBy<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.EndedBy::new, time1, time2); } /** @@@ -813,11 -934,14 +816,11 @@@ * @param time1 expression fetching the first temporal value. * @param time2 expression fetching the second temporal value. * @return a filter for the "AnyInteracts" operator between the two temporal values. - * - * @see TemporalOperatorName#ANY_INTERACTS */ - @Override - public TemporalOperator<R> anyInteracts(final Expression<R, ? extends T> time1, - final Expression<R, ? extends T> time2) + public Filter<R> anyInteracts(final Expression<R, ? extends T> time1, + final Expression<R, ? extends T> time2) { - return new TemporalFilter.AnyInteracts<>(time1, time2); + return TemporalFilter.create(temporal, TemporalOperation.AnyInteracts::new, time1, time2); } /** diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TemporalFilter.java index ae78bad713,88ebd77415..21629a0fda --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TemporalFilter.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TemporalFilter.java @@@ -16,11 -16,15 +16,12 @@@ */ package org.apache.sis.filter; - import java.time.Instant; + import org.apache.sis.util.Classes; + import org.apache.sis.feature.privy.FeatureExpression; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.temporal.Period; -import org.opengis.filter.Filter; -import org.opengis.filter.Expression; -import org.opengis.filter.TemporalOperator; -import org.opengis.filter.TemporalOperatorName; +// Specific to the main branch: +import org.apache.sis.pending.geoapi.temporal.Period; +import org.apache.sis.pending.geoapi.filter.TemporalOperatorName; /** @@@ -37,10 -35,11 +32,11 @@@ * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) * - * @param <T> the type of resources (e.g. {@code Feature}) used as inputs. - * @param <R> the type of resources (e.g. {@link org.opengis.feature.Feature}) used as inputs. ++ * @param <R> the type of resources (e.g. {@code Feature}) used as inputs. + * @param <T> the base type of temporal objects, or {@code Object.class} for any type. */ - abstract class TemporalFilter<T> extends BinaryFunction<T,Object,Object> - implements Filter<T>, Optimization.OnFilter<T> + class TemporalFilter<R,T> extends BinaryFunction<R,T,T> - implements TemporalOperator<R>, Optimization.OnFilter<R> ++ implements Filter<R>, Optimization.OnFilter<R> { /** * For cross-version compatibility. diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TemporalOperation.java index 0000000000,d826f84485..535990fc99 mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TemporalOperation.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TemporalOperation.java @@@ -1,0 -1,762 +1,762 @@@ + /* + * 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.filter; + + import java.io.Serializable; + import java.time.temporal.Temporal; + import org.apache.sis.util.Classes; + import org.apache.sis.util.privy.Strings; + import org.apache.sis.util.collection.WeakHashSet; + import static org.apache.sis.filter.TimeMethods.BEFORE; + import static org.apache.sis.filter.TimeMethods.AFTER; + import static org.apache.sis.filter.TimeMethods.EQUAL; + + // Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.temporal.Period; -import org.opengis.filter.TemporalOperatorName; ++import org.apache.sis.pending.geoapi.temporal.Period; ++import org.apache.sis.pending.geoapi.filter.TemporalOperatorName; + + + /** + * Temporal operations between periods and/or instants. + * The nature of the operation depends on the subclass. + * Subclasses shall override at least one of the following methods: + * + * <ul> + * <li>{@link #evaluate(T, T)}</li> + * <li>{@link #evaluate(T, Period)}</li> + * <li>{@link #evaluate(Period, T)}</li> + * <li>{@link #evaluate(Period, Period)}</li> + * </ul> + * + * Instances of this classes are immutable and thread-safe. + * The same instances are shared by many filters. + * + * @author Johann Sorel (Geomatys) + * @author Martin Desruisseaux (Geomatys) + * + * @param <T> the base type of temporal objects, or {@code Object.class} for any type. + */ + abstract class TemporalOperation<T> implements Serializable { + /** + * For cross-version compatibility. + */ + private static final long serialVersionUID = -5304814915639460679L; + + /** + * Temporal operations created in the current JVM. + */ + @SuppressWarnings("unchecked") + private static final WeakHashSet<TemporalOperation<?>> POOL = new WeakHashSet<>((Class) TemporalOperation.class); + + /** + * The set of methods to invoke for performing "is before", "is after" or "is equal" comparisons. + */ + protected final TimeMethods<T> comparators; + + /** + * Creates a new temporal operation. + * + * @param comparators the set of methods to invoke for performing comparisons. + */ + protected TemporalOperation(final TimeMethods<T> comparators) { + this.comparators = comparators; + } + + /** + * Returns a unique, shared instance of this operation. + * + * @return a unique instance equal to {@code this}. + */ + public final TemporalOperation<T> unique() { + return POOL.unique(this); + } + + /** + * Returns a hash code value for this operation. + * Used for {@link #unique()} implementation. + */ + @Override + public final int hashCode() { + return getClass().hashCode() + 31 * comparators.type.hashCode(); + } + + /** + * Compares the given object with this operation for equality. + * Used for {@link #unique()} implementation. + */ + @Override + public final boolean equals(final Object other) { + return (other != null) && (other.getClass() == getClass()) && + ((TemporalOperation) other).comparators.type == comparators.type; + } + + /** + * Returns a string representation for debugging purposes. + */ + @Override + public final String toString() { + return Strings.toString(getClass(), + "operator", getOperatorType().identifier(), + "type", comparators.type.getSimpleName()); + } + + /** + * Returns an identification of this operation. + */ + public abstract TemporalOperatorName getOperatorType(); + + /** + * Returns the mathematical symbol for this temporal operation. + * + * @return the mathematical symbol, or 0 if none. + */ + protected char symbol() { + return (char) 0; + } + + /** + * Evaluates the filter between two temporal objects. + * Both arguments given to this method shall be non-null. + */ + protected boolean evaluate(T self, T other) { + return false; + } + + /** + * Evaluates the filter between a temporal object and a period. + * Both arguments given to this method shall be non-null, but period begin or end instant may be null. + * + * <p><b>Note:</b> this relationship is not defined by ISO 19108. This method should be overridden + * only when an ISO 19108 extension can be easily defined, for example for the "equal" operation.</p> + */ + protected boolean evaluate(T self, Period other) { + return false; + } + + /** + * Evaluates the filter between a period and a temporal object. + * Both arguments given to this method shall be non-null, but period begin or end instant may be null. + * Note: the {@code self} and {@code other} argument names are chosen to match ISO 19108 tables. + */ + protected boolean evaluate(Period self, T other) { + return false; + } + + /** + * Evaluates the filter between two periods. + * Both arguments given to this method shall be non-null, but period begin or end instant may be null. + * Note: the {@code self} and {@code other} argument names are chosen to match ISO 19108 tables. + */ + protected abstract boolean evaluate(Period self, Period other); + + /** + * Returns {@code true} if {@code other} is non-null and the specified comparison evaluates to {@code true}. + * This is a helper function for {@code evaluate(…)} methods implementations. + * + * @param test enumeration value such as {@link TimeMethods#BEFORE} or {@link TimeMethods#AFTER}. + * @param self the object on which to invoke the method identified by {@code test}. + * @param other the argument to give to the test method call, or {@code null} if none. + * @return the result of performing the comparison identified by {@code test}. - * @throws InvalidFilterValueException if the two objects cannot be compared. ++ * @throws IllegalArgumentException if the two objects cannot be compared. + */ + final boolean compare(final int test, final T self, final Temporal other) { + return (other != null) && comparators.compare(test, self, other); + } + + /** + * Returns {@code true} if both arguments are non-null and the specified comparison evaluates to {@code true}. + * This is a helper function for {@code evaluate(…)} methods implementations. + * + * @param test enumeration value such as {@link TimeMethods#BEFORE} or {@link TimeMethods#AFTER}. + * @param self the object on which to invoke the method identified by {@code test}, or {@code null} if none. + * @param other the argument to give to the test method call, or {@code null} if none. + * @return the result of performing the comparison identified by {@code test}. - * @throws InvalidFilterValueException if the two objects cannot be compared. ++ * @throws IllegalArgumentException if the two objects cannot be compared. + */ + @SuppressWarnings("unchecked") + static boolean compare(final int test, final Temporal self, final Temporal other) { + return (self != null) && (other != null) && TimeMethods.compare(test, + (Class) Classes.findCommonClass(self.getClass(), other.getClass()), self, other); + } + + + /** + * Reference to a sub-class constructor. + * + * @param <T> type of temporal object. + */ + @FunctionalInterface + static interface Factory { + /** + * Creates a new temporal operation. + * + * @param <T> type of temporal objects that the operation will accept. + * @param comparators the set of methods to invoke for performing comparisons. + * @return the temporal operation using the given "is before", "is after" and "is equal" methods. + */ + <T> TemporalOperation<T> create(TimeMethods<T> comparators); + } + + + /** + * The {@code "TEquals"} (=) operation. Defined by ISO 19108 as: + * <ul> + * <li>{@literal self = other}</li> + * <li>{@literal self.begin = other.begin AND self.end = other.end}</li> + * </ul> + * + * @param <T> the base type of temporal objects. + */ + static final class Equals<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = -6060822291802339424L; + + /** Creates a new operation. */ + Equals(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.EQUALS; + } + + /** Symbol of this operation. */ + @Override protected char symbol() { + return '='; + } + + /** Condition defined by ISO 19108:2002 §5.2.3.5. */ + @Override protected boolean evaluate(T self, T other) { + return comparators.isEqual.test(self, other); + } + + /** Extension to ISO 19108: handle instant as a tiny period. */ + @Override public boolean evaluate(T self, Period other) { + return compare(EQUAL, self, other.getBeginning()) && + compare(EQUAL, self, other.getEnding()); + } + + /** Extension to ISO 19108: handle instant as a tiny period. */ + @Override public boolean evaluate(Period self, T other) { + return compare(EQUAL, other, self.getBeginning()) && + compare(EQUAL, other, self.getEnding()); + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(Period self, Period other) { + return compare(EQUAL, self.getBeginning(), other.getBeginning()) && + compare(EQUAL, self.getEnding(), other.getEnding()); + } + } + + + /** + * The {@code "Before"} {@literal (<)} operation. Defined by ISO 19108 as: + * <ul> + * <li>{@literal self < other}</li> + * <li>{@literal self.end < other}</li> + * <li>{@literal self.end < other.begin}</li> + * </ul> + * + * @param <T> the base type of temporal objects. + */ + static final class Before<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = -3422629447456003982L; + + /** Creates a new operation. */ + Before(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.BEFORE; + } + + /** Symbol of this operation. */ + @Override protected char symbol() { + return '<'; + } + + /** Condition defined by ISO 19108:2002 §5.2.3.5. */ + @Override protected boolean evaluate(T self, T other) { + return comparators.isBefore.test(self, other); + } + + /** Relationship not defined by ISO 19108:2006. */ + @Override public boolean evaluate(T self, Period other) { + return compare(BEFORE, self, other.getBeginning()); + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(Period self, T other) { + return compare(AFTER, other, self.getEnding()); + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(Period self, Period other) { + return compare(BEFORE, self.getEnding(), other.getBeginning()); + } + } + + + /** + * The {@code "After"} {@literal (>)} operation. Defined by ISO 19108 as: + * <ul> + * <li>{@literal self > other}</li> + * <li>{@literal self.begin > other}</li> + * <li>{@literal self.begin > other.end}</li> + * </ul> + * + * @param <T> the base type of temporal objects. + */ + static final class After<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = 5410476260417497682L; + + /** Creates a new operation. */ + After(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.AFTER; + } + + /** Symbol of this operation. */ + @Override protected char symbol() { + return '>'; + } + + /** Condition defined by ISO 19108:2002 §5.2.3.5. */ + @Override protected boolean evaluate(T self, T other) { + return comparators.isAfter.test(self, other); + } + + /** Relationship not defined by ISO 19108:2006. */ + @Override public boolean evaluate(T self, Period other) { + return compare(AFTER, self, other.getEnding()); + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(Period self, T other) { + return compare(BEFORE, other, self.getBeginning()); + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(Period self, Period other) { + return compare(AFTER, self.getBeginning(), other.getEnding()); + } + } + + + /** + * The {@code "Begins"} operation. Defined by ISO 19108 as: + * <ul> + * <li>{@literal self.begin = other.begin AND self.end < other.end}</li> + * </ul> + * + * @param <T> the base type of temporal objects. + */ + static final class Begins<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = -7880699329127762233L; + + /** Creates a new operation. */ + Begins(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.BEGINS; + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(final Period self, final Period other) { + return compare(EQUAL, self.getBeginning(), other.getBeginning()) && + compare(BEFORE, self.getEnding(), other.getEnding()); + } + } + + + /** + * The {@code "Ends"} operation. Defined by ISO 19108 as: + * <ul> + * <li>{@literal self.begin > other.begin AND self.end = other.end}</li> + * </ul> + * + * @param <T> the base type of temporal objects. + */ + static final class Ends<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = -5508229966320563437L; + + /** Creates a new operation. */ + Ends(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.ENDS; + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(final Period self, final Period other) { + return compare(EQUAL, self.getEnding(), other.getEnding()) && + compare(AFTER, self.getBeginning(), other.getBeginning()); + } + } + + + /** + * The {@code "BegunBy"} operation. Defined by ISO 19108 as: + * <ul> + * <li>{@literal self.begin = other}</li> + * <li>{@literal self.begin = other.begin AND self.end > other.end}</li> + * </ul> + * + * @param <T> the base type of temporal objects. + */ + static final class BegunBy<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = -7212413827394364384L; + + /** Creates a new operation. */ + BegunBy(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.BEGUN_BY; + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(Period self, T other) { + return compare(EQUAL, other, self.getBeginning()); + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(final Period self, final Period other) { + return compare(EQUAL, self.getBeginning(), other.getBeginning()) && + compare(AFTER, self.getEnding(), other.getEnding()); + } + } + + + /** + * The {@code "EndedBy"} operation. Defined by ISO 19108 as: + * <ul> + * <li>{@literal self.end = other}</li> + * <li>{@literal self.begin < other.begin AND self.end = other.end}</li> + * </ul> + * + * @param <T> the base type of temporal objects. + */ + static final class EndedBy<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = 8586566103462153666L; + + /** Creates a new operation. */ + EndedBy(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.ENDED_BY; + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(final Period self, final T other) { + return compare(EQUAL, other, self.getEnding()); + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(final Period self, final Period other) { + return compare(EQUAL, self.getEnding(), other.getEnding()) && + compare(BEFORE, self.getBeginning(), other.getBeginning()); + } + } + + + /** + * The {@code "Meets"} operation. Defined by ISO 19108 as: + * <ul> + * <li>{@literal self.end = other.begin}</li> + * </ul> + * + * @param <T> the base type of temporal objects. + */ + static final class Meets<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = -3534843269384858443L; + + /** Creates a new operation. */ + Meets(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.MEETS; + } + + /** Extension to ISO 19108: handle instant as a tiny period. */ + @Override public boolean evaluate(final T self, final T other) { + return comparators.isEqual.test(self, other); + } + + /** Extension to ISO 19108: handle instant as a tiny period. */ + @Override public boolean evaluate(final T self, final Period other) { + return compare(EQUAL, self, other.getBeginning()); + } + + /** Extension to ISO 19108: handle instant as a tiny period. */ + @Override public boolean evaluate(final Period self, final T other) { + return compare(EQUAL, other, self.getEnding()); + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(final Period self, final Period other) { + return compare(EQUAL, self.getEnding(), other.getBeginning()); + } + } + + + /** + * The {@code "MetBy"} operation. Defined by ISO 19108 as: + * <ul> + * <li>{@literal self.begin = other.end}</li> + * </ul> + * + * @param <T> the base type of temporal objects. + */ + static final class MetBy<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = 5358059498707330482L; + + /** Creates a new operation. */ + MetBy(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.MET_BY; + } + + /** Extension to ISO 19108: handle instant as a tiny period. */ + @Override public boolean evaluate(final T self, final T other) { + return comparators.isEqual.test(self, other); + } + + /** Extension to ISO 19108: handle instant as a tiny period. */ + @Override public boolean evaluate(final T self, final Period other) { + return compare(EQUAL, self, other.getEnding()); + } + + /** Extension to ISO 19108: handle instant as a tiny period. */ + @Override public boolean evaluate(final Period self, final T other) { + return compare(EQUAL, other, self.getBeginning()); + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(final Period self, final Period other) { + return compare(EQUAL, self.getBeginning(), other.getEnding()); + } + } + + + /** + * The {@code "During"} operation. Defined by ISO 19108 as: + * <ul> + * <li>{@literal self.begin > other.begin AND self.end < other.end}</li> + * </ul> + * + * @param <T> the base type of temporal objects. + */ + static final class During<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = -4674319635076886196L; + + /** Creates a new operation. */ + During(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.DURING; + } + + /** Symbol of this operation. */ + @Override protected char symbol() { + return '⊊'; // `self` is a proper (or strict) subset of `other`. + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(final Period self, final Period other) { + return compare(AFTER, self.getBeginning(), other.getBeginning()) && + compare(BEFORE, self.getEnding(), other.getEnding()); + } + } + + + /** + * The {@code "TContains"} operation. Defined by ISO 19108 as: + * <ul> + * <li>{@literal self.begin < other AND self.end > other}</li> + * <li>{@literal self.begin < other.begin AND self.end > other.end}</li> + * </ul> + * + * @param <T> the base type of temporal objects. + */ + static final class Contains<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = 9107531246948034411L; + + /** Creates a new operation. */ + Contains(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.CONTAINS; + } + + /** Symbol of this operation. */ + @Override protected char symbol() { + return '⊋'; // `self` is a proper (or strict) superset of `other`. + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(final Period self, final T other) { + return compare(AFTER, other, self.getBeginning()) && + compare(BEFORE, other, self.getEnding()); + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(final Period self, final Period other) { + return compare(BEFORE, self.getBeginning(), other.getBeginning()) && + compare(AFTER, self.getEnding(), other.getEnding()); + } + } + + + /** + * The {@code "TOverlaps"} operation. Defined by ISO 19108 as: + * <ul> + * <li>{@literal self.begin < other.begin AND self.end > other.begin AND self.end < other.end}</li> + * </ul> + * + * @param <T> the base type of temporal objects. + */ + static final class Overlaps<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = 1517443045593389773L; + + /** Creates a new operation. */ + Overlaps(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.OVERLAPS; + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(final Period self, final Period other) { + final Temporal selfBegin, selfEnd, otherBegin, otherEnd; + return ((otherBegin = other.getBeginning()) != null) && + ((selfBegin = self .getBeginning()) != null) && compare(BEFORE, selfBegin, otherBegin) && + ((selfEnd = self .getEnding()) != null) && compare(AFTER, selfEnd, otherBegin) && + ((otherEnd = other.getEnding()) != null) && compare(BEFORE, selfEnd, otherEnd); + } + } + + + /** + * The {@code "OverlappedBy"} operation. Defined by ISO 19108 as: + * <ul> + * <li>{@literal self.begin > other.begin AND self.begin < other.end AND self.end > other.end}</li> + * </ul> + * + * @param <T> the base type of temporal objects. + */ + static final class OverlappedBy<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = 2228673820507226463L; + + /** Creates a new operation. */ + OverlappedBy(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.OVERLAPPED_BY; + } + + /** Condition defined by ISO 19108:2006 (corrigendum) §5.2.3.5. */ + @Override public boolean evaluate(final Period self, final Period other) { + final Temporal selfBegin, selfEnd, otherBegin, otherEnd; + return ((selfBegin = self .getBeginning()) != null) && + ((otherBegin = other.getBeginning()) != null) && compare(AFTER, selfBegin, otherBegin) && + ((otherEnd = other.getEnding()) != null) && compare(BEFORE, selfBegin, otherEnd) && + ((selfEnd = self .getEnding()) != null) && compare(AFTER, selfEnd, otherEnd); + } + } + + + /** + * The {@code "AnyInteracts"} filter. + * This is a shortcut for NOT (Before OR Meets OR MetBy OR After). + * + * @param <T> the base type of temporal objects. + */ + static final class AnyInteracts<T> extends TemporalOperation<T> { + /** For cross-version compatibility during (de)serialization. */ + private static final long serialVersionUID = 5972351564286442392L; + + /** Creates a new operation. */ + AnyInteracts(TimeMethods<T> comparators) { + super(comparators); + } + + /** Identification of this operation. */ + @Override public TemporalOperatorName getOperatorType() { + return TemporalOperatorName.ANY_INTERACTS; + } + + /** Condition defined by OGC filter specification. */ + @Override public boolean evaluate(final Period self, final Period other) { + final Temporal selfBegin, selfEnd, otherBegin, otherEnd; + return ((selfBegin = self .getBeginning()) != null) && + ((otherEnd = other.getEnding()) != null) && compare(BEFORE, selfBegin, otherEnd) && + ((selfEnd = self .getEnding()) != null) && + ((otherBegin = other.getBeginning()) != null) && compare(AFTER, selfEnd, otherBegin); + } + } + } diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TimeMethods.java index 0000000000,11cc0ca6b7..18d60debd9 mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TimeMethods.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TimeMethods.java @@@ -1,0 -1,396 +1,393 @@@ + /* + * 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.filter; + + import java.util.Map; + import java.util.Date; + import java.time.Instant; + import java.time.Year; + import java.time.YearMonth; + import java.time.MonthDay; + import java.time.LocalTime; + import java.time.OffsetTime; + import java.time.OffsetDateTime; + import java.time.DateTimeException; + import java.time.chrono.ChronoLocalDate; + import java.time.chrono.ChronoLocalDateTime; + import java.time.chrono.ChronoZonedDateTime; + import java.time.temporal.ChronoField; + import java.time.temporal.Temporal; + import java.time.temporal.TemporalAccessor; + import java.util.function.BiPredicate; + import java.lang.reflect.Modifier; + import java.io.Serializable; + import java.io.ObjectStreamException; + import org.apache.sis.util.privy.Strings; + import org.apache.sis.util.resources.Errors; + -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.filter.InvalidFilterValueException; - + + /** + * Provides the <i>is before</i> and <i>is after</i> operations for various {@code java.time} objects. + * This class delegates to the {@code isBefore(T)} or {@code isAfter(T)} methods of each supported classes. + * + * Instances of this classes are immutable and thread-safe. + * The same instance can be shared by many {@link TemporalOperation} instances. + * + * <h2>Design note about alternative approaches</h2> + * We do not delegate to {@link Comparable#compareTo(Object)} because the latter method compares not only + * positions on the timeline, but also other properties not relevant to an "is before" or "is after" test. + * We could use {@link ChronoLocalDate#timeLineOrder()} comparators instead, but those comparators are not + * defined for every classes where the "is before" and "is after" methods differ from "compare to" method. + * Furthermore, some temporal classes override {@code isBefore(T)} or {@code isAfter(T)} for performance. + * + * @param <T> the base type of temporal objects, or {@code Object.class} for any type. + * + * @author Martin Desruisseaux (Geomatys) + */ + class TimeMethods<T> implements Serializable { + /** + * For cross-version compatibility. + */ + private static final long serialVersionUID = 1075289362575825939L; + + /** + * The type of temporal objects accepted by this set of operations. + */ + public final Class<T> type; + + /** + * Enumeration values for a test to apply. + * + * @see #compare(int, T, TemporalAccessor) + */ + static final int BEFORE=1, AFTER=2, EQUAL=0; + + /** + * Predicate to execute for testing the ordering between temporal objects. + * This comparison operator differs from the {@code compareTo(…)} method in that it compares only the + * positions on the timeline, ignoring metadata such as the calendar used for representing positions. + */ + public final transient BiPredicate<T,T> isBefore, isAfter, isEqual; + + /** + * Creates a new set of operators. This method is for subclasses only. + * For getting a {@code TimeMethods} instance, see {@link #find(Class)}. + */ + private TimeMethods(final Class<T> type, + final BiPredicate<T,T> isBefore, + final BiPredicate<T,T> isAfter, + final BiPredicate<T,T> isEqual) + { + this.type = type; + this.isBefore = isBefore; + this.isAfter = isAfter; + this.isEqual = isEqual; + } + + /** + * Returns whether the end point will be determined dynamically every time that a method is invoked. + * + * @return whether the methods are determined dynamically on an instance-by-instance basis. + */ + public boolean isDynamic() { + return false; + } + + /** + * Delegates the comparison to the method identified by the {@code test} argument. + * This method is overridden in subclasses where the delegation can be more direct. + * + * @param test {@link #BEFORE}, {@link #AFTER} or {@link #EQUAL}. + * @param self the object on which to invoke the method identified by {@code test}. + * @param other the argument to give to the test method call. + * @return the result of performing the comparison identified by {@code test}. + */ + boolean delegate(final int test, final T self, final T other) { + final BiPredicate<T,T> p; + switch (test) { + case BEFORE: p = isBefore; break; + case AFTER: p = isAfter; break; + case EQUAL: p = isEqual; break; + default: throw new AssertionError(test); + } + return p.test(self, other); + } + + /** + * Compares an object of class {@code <T>} with a temporal object of unknown class. + * The other object is typically the beginning or ending of a period. + * + * @param test {@link #BEFORE}, {@link #AFTER} or {@link #EQUAL}. + * @param self the object on which to invoke the method identified by {@code test}. + * @param other the argument to give to the test method call. + * @return the result of performing the comparison identified by {@code test}. - * @throws InvalidFilterValueException if the two objects cannot be compared. ++ * @throws IllegalArgumentException if the two objects cannot be compared. + */ + @SuppressWarnings("unchecked") + final boolean compare(final int test, final T self, final TemporalAccessor other) { + if (type.isInstance(other)) { + return delegate(test, self, (T) other); // Safe because of above `isInstance(…)` check. + } + return compareAsInstants(test, accessor(self), other); + } + + /** + * Compares two temporal objects of unknown class. This method needs to check for specialized implementations + * before to delegate to {@link Comparable#compareTo(Object)}, because the comparison methods on the timeline + * are not always the same as {@code compareTo(…)}. + * + * @param <T> base class of the objects to compare. + * @param test {@link #BEFORE}, {@link #AFTER} or {@link #EQUAL}. + * @param type base class of the {@code self} and {@code other} arguments. + * @param self the object on which to invoke the method identified by {@code test}. + * @param other the argument to give to the test method call. + * @return the result of performing the comparison identified by {@code test}. - * @throws InvalidFilterValueException if the two objects cannot be compared. ++ * @throws IllegalArgumentException if the two objects cannot be compared. + */ + static <T> boolean compare(final int test, final Class<T> type, final T self, final T other) { + /* + * The following cast is not strictly true, it should be `<? extends T>`. + * However, because of the `isInstance(…)` check and because <T> is used + * only as parameter type (no collection), it is okay to use it that way. + */ + final TimeMethods<? super T> tc = findSpecialized(type); + if (tc != null) { + /* + * Found one of the special cases listed in `INTERFACES` or `FINAL_TYPE`. + * If the other type is compatible, the comparison is executed directly. + * Note: the `switch` statement is equivalent to `tc.compare(test, …)`, + * but is inlined because that method is never overridden in this context. + */ + if (tc.type.isInstance(other)) { + assert tc.type.isAssignableFrom(type) : tc; // Those types are not necessarily equal. + final BiPredicate<? super T, ? super T> p; + switch (test) { + case BEFORE: p = tc.isBefore; break; + case AFTER: p = tc.isAfter; break; + case EQUAL: p = tc.isEqual; break; + default: throw new AssertionError(test); + } + return p.test(self, other); + } + } else if (self instanceof Comparable<?> && type.isInstance(other)) { + /* + * The type of the first operand is not a special case, but the second operand is compatible + * for a call to the generic `compareTo(…)` method. This case does not happen often, because + * not many `java.time` classes have no "is before" or "is after" operations. + * Some examples are `Month` and `DayOfWeek`. + */ + @SuppressWarnings("unchecked") // Safe because verification done by `isInstance(…)`. + final int c = ((Comparable) self).compareTo(other); + switch (test) { + case BEFORE: return c < 0; + case AFTER: return c > 0; + case EQUAL: return c == 0; + default: throw new AssertionError(test); + } + } + /* + * If we reach this point, the two operands are of different classes and we cannot compare them directly. + * Try to compare the two operands as instants on the timeline. + */ + return compareAsInstants(test, accessor(self), accessor(other)); + } + + /** + * Returns the given object as a temporal accessor. + */ + private static TemporalAccessor accessor(final Object value) { + if (value instanceof TemporalAccessor) { + return (TemporalAccessor) value; + } else if (value instanceof Date) { + return ((Date) value).toInstant(); // Overridden in `Date` subclasses. + } else { - throw new InvalidFilterValueException(Errors.format( ++ throw new IllegalArgumentException(Errors.format( + Errors.Keys.CannotCompareInstanceOf_2, value.getClass(), TemporalAccessor.class)); + } + } + + /** + * Compares two temporal objects as instants. + * This is a last-resort fallback, when objects cannot be compared by their own methods. + * + * @param test {@link #BEFORE}, {@link #AFTER} or {@link #EQUAL}. + * @param self the object on which to invoke the method identified by {@code test}. + * @param other the argument to give to the test method call. + * @return the result of performing the comparison identified by {@code test}. - * @throws InvalidFilterValueException if the two objects cannot be compared. ++ * @throws IllegalArgumentException if the two objects cannot be compared. + */ + private static boolean compareAsInstants(final int test, final TemporalAccessor self, final TemporalAccessor other) { + try { + long t1 = self.getLong(ChronoField.INSTANT_SECONDS); + long t2 = other.getLong(ChronoField.INSTANT_SECONDS); + if (t1 == t2) { + t1 = self.getLong(ChronoField.NANO_OF_SECOND); // Should be present according Javadoc. + t2 = other.getLong(ChronoField.NANO_OF_SECOND); + if (t1 == t2) { + return test == EQUAL; + } + } + return test == ((t1 < t2) ? BEFORE : AFTER); + } catch (DateTimeException | ArithmeticException e) { - throw new InvalidFilterValueException(Errors.format( ++ throw new IllegalArgumentException(Errors.format( + Errors.Keys.CannotCompareInstanceOf_2, self.getClass(), other.getClass()), e); + } + } + + /** + * Returns the set of methods that can be invoked on instances of the given type, or {@code null} if none. + * This method returns only one of the methods defined in {@link #FINAL_TYPES} or {@link #INTERFACES}. + * It shall not try to create fallbacks. + * + * @param <T> compile-time value of the {@code type} argument. + * @param type the type of temporal object for which to get specialized methods. + * @return set of specialized methods for the given object type, or {@code null} if none. + */ + @SuppressWarnings("unchecked") + private static <T> TimeMethods<? super T> findSpecialized(final Class<T> type) { + { // Block for keeping `tc` in local scope. + TimeMethods<?> tc = FINAL_TYPES.get(type); + if (tc != null) { + assert tc.type == type : tc; + return (TimeMethods<T>) tc; // Safe because of `==` checks. + } + } + for (TimeMethods<?> tc : INTERFACES) { + if (tc.type.isAssignableFrom(type)) { + return (TimeMethods<? super T>) tc; // Safe because of `isAssignableFrom(…)` checks. + } + } + return null; + } + + /** + * Returns the set of methods that can be invoked on instances of the given type. + * + * @param <T> compile-time value of the {@code type} argument. + * @param type the type of temporal object for which to get specialized methods. + * @return set of comparison methods for the given object type. + */ + @SuppressWarnings("unchecked") // For (Comparable) casts. + public static <T> TimeMethods<? super T> find(final Class<T> type) { + final TimeMethods<? super T> tc = findSpecialized(type); + if (tc != null) { + return tc; + } + if (Modifier.isFinal(type.getModifiers())) { + if (Comparable.class.isAssignableFrom(type)) { + return new TimeMethods<>(type, + (self, other) -> ((Comparable) self).compareTo(other) < 0, + (self, other) -> ((Comparable) self).compareTo(other) > 0, + (self, other) -> ((Comparable) self).compareTo(other) == 0); + } else { - throw new InvalidFilterValueException(Errors.format(Errors.Keys.CannotCompareInstanceOf_2, type, type)); ++ throw new IllegalArgumentException(Errors.format(Errors.Keys.CannotCompareInstanceOf_2, type, type)); + } + } else { + return fallback(type); + } + } + + /** + * Returns the last-resort fallback when the type of temporal objects cannot be determined in advance. + * + * @param <T> compile-time value of the {@code type} argument. + * @param type the type of temporal object for which to get the last-resource fallback methods. + * @return set of last-resort comparison methods for the given object type. + */ + private static <T> TimeMethods<? super T> fallback(final Class<T> type) { + return new TimeMethods<>(type, + (self, other) -> compare(BEFORE, type, self, other), + (self, other) -> compare(AFTER, type, self, other), + (self, other) -> compare(EQUAL, type, self, other)) + { + @Override public boolean isDynamic() { + return true; + } + @Override boolean delegate(final int test, final T self, final T other) { + return compare(test, type, self, other); + } + }; + } + + /** + * Returns the unique instance for the type after deserialization. + * This is needed for avoiding to serialize the lambda functions. + * + * @return the object to use after deserialization. + * @throws ObjectStreamException if the serialized object contains invalid data. + */ + private Object readResolve() throws ObjectStreamException { + return find(type); + } + + /** + * Operators for all supported temporal types that are interfaces or non-final classes. + * Those types need to be checked with {@link Class#isAssignableFrom(Class)} in iteration order. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) // For `Chrono*` interfaces, because they are parameterized. + private static final TimeMethods<?>[] INTERFACES = { + new TimeMethods<>(ChronoZonedDateTime.class, ChronoZonedDateTime::isBefore, ChronoZonedDateTime::isAfter, ChronoZonedDateTime::isEqual), + new TimeMethods<>(ChronoLocalDateTime.class, ChronoLocalDateTime::isBefore, ChronoLocalDateTime::isAfter, ChronoLocalDateTime::isEqual), + new TimeMethods<>( ChronoLocalDate.class, ChronoLocalDate::isBefore, ChronoLocalDate::isAfter, ChronoLocalDate::isEqual), + new TimeMethods<>( Date.class, Date:: before, Date:: after, Date::equals) + }; + + /* + * No operation on numbers for now. We could revisit this policy in a future version if we + * allow the temporal function to have a CRS and to operate on temporal coordinate values. + */ + + /** + * Operators for all supported temporal types for which there is no need to check for subclasses. + * Those classes are usually final, except when wanting to intentionally ignore all subclasses. + * Those types should be tested before {@link #INTERFACES} because this check is quick. + * + * <h4>Implementation note</h4> + * {@link Year}, {@link YearMonth}, {@link MonthDay}, {@link LocalTime} and {@link Instant} + * could be replaced by {@link Comparable}. We nevertheless keep the specialized classes in + * case the implementations change in the future, and also for performance reason, because + * the code working on generic {@link Comparable} needs to check for special cases again. + */ + private static final Map<Class<?>, TimeMethods<?>> FINAL_TYPES = Map.ofEntries( + entry(new TimeMethods<>(OffsetDateTime.class, OffsetDateTime::isBefore, OffsetDateTime::isAfter, OffsetDateTime::isEqual)), + entry(new TimeMethods<>( OffsetTime.class, OffsetTime::isBefore, OffsetTime::isAfter, OffsetTime::isEqual)), + entry(new TimeMethods<>( LocalTime.class, LocalTime::isBefore, LocalTime::isAfter, LocalTime::equals)), + entry(new TimeMethods<>( Year.class, Year::isBefore, Year::isAfter, Year::equals)), + entry(new TimeMethods<>( YearMonth.class, YearMonth::isBefore, YearMonth::isAfter, YearMonth::equals)), + entry(new TimeMethods<>( MonthDay.class, MonthDay::isBefore, MonthDay::isAfter, MonthDay::equals)), + entry(new TimeMethods<>( Instant.class, Instant::isBefore, Instant::isAfter, Instant::equals)), + entry(fallback(Temporal.class)), // Frequently declared type. Intentionally no "instance of" checks. + entry(fallback(Object.class))); // Not a final class, but to be used when the declared type is Object. + + /** + * Helper method for adding entries to the {@link #FINAL_TYPES} map. + * Shall be used only for final classes. + */ + private static Map.Entry<Class<?>, TimeMethods<?>> entry(final TimeMethods<?> op) { + return Map.entry(op.type, op); + } + + /** + * Returns a string representation of this set of operations for debugging purposes. + * + * @return a string representation for debugging purposes. + */ + @Override + public String toString() { + return Strings.toString(TimeMethods.class, "type", type.getSimpleName()); + } + } diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/pending/geoapi/filter/TemporalOperatorName.java index ddb62b817a,f35ee94813..a30135526f --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/pending/geoapi/filter/TemporalOperatorName.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/pending/geoapi/filter/TemporalOperatorName.java @@@ -18,11 -18,21 +18,15 @@@ package org.apache.sis.pending.geoapi.f /** - * Exception that may be thrown by a portraying operation. - * - * @author Johann Sorel (Geomatys) + * Placeholder for GeoAPI 3.1 interfaces (not yet released). + * Shall not be visible in public API, as it will be deleted after next GeoAPI release. */ -public class RenderingException extends Exception { - - public RenderingException(String message) { - super(message); - } - - public RenderingException(Throwable cause) { - super(cause); - } +@SuppressWarnings("doclint:missing") +public enum TemporalOperatorName { + AFTER, BEFORE, BEGINS, BEGUN_BY, CONTAINS, DURING, EQUALS, OVERLAPS, MEETS, ENDS, + OVERLAPPED_BY, MET_BY, ENDED_BY, ANY_INTERACTS; + - public RenderingException(String message, Throwable cause) { - super(message, cause); ++ public String identifier() { ++ return name().toLowerCase(); + } } diff --cc endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/TemporalFilterTest.java index 71c23abbd6,b349e62672..a07fd7b129 --- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/TemporalFilterTest.java +++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/TemporalFilterTest.java @@@ -22,12 -24,14 +24,12 @@@ import static org.apache.sis.util.privy import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; import org.apache.sis.test.TestCase; - import org.apache.sis.test.TestUtilities; import static org.apache.sis.test.Assertions.assertSerializedEquals; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.temporal.Period; -import org.opengis.feature.Feature; -import org.opengis.filter.FilterFactory; -import org.opengis.filter.TemporalOperator; -import org.opengis.filter.TemporalOperatorName; +// Specific to the main branch: +import org.apache.sis.feature.AbstractFeature; ++import org.apache.sis.pending.geoapi.temporal.Period; +import org.apache.sis.pending.geoapi.filter.TemporalOperatorName; /** @@@ -40,7 -44,7 +42,7 @@@ public final class TemporalFilterTest e /** * The factory to use for creating the objects to test. */ - private final DefaultFilterFactory<AbstractFeature,Object,Object> factory; - private FilterFactory<Feature, Object, ? super Period> factory; ++ private DefaultFilterFactory<AbstractFeature, Object, ? super Period> factory; /** * The filter to test. This field shall be assigned by each {@code testFoo()} method by invoking diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/citation/DefaultCitation.java index b3251941cf,7f06cf9de3..26e877abe8 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/citation/DefaultCitation.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/citation/DefaultCitation.java @@@ -211,6 -206,7 +211,7 @@@ public class DefaultCitation extends IS * * @see #castOrCopy(Citation) */ - @SuppressWarnings({"this-escape", "deprecation"}) ++ @SuppressWarnings("this-escape") public DefaultCitation(final Citation object) { super(object); if (object != null) { diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultStandardOrderProcess.java index a4e68742e0,2991ced2ec..6689ee1496 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultStandardOrderProcess.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultStandardOrderProcess.java @@@ -29,14 -30,8 +30,13 @@@ import org.opengis.metadata.distributio import org.apache.sis.xml.bind.gco.GO_RecordType; import org.apache.sis.xml.bind.gco.GO_Record; import org.apache.sis.metadata.iso.ISOMetadata; - import static org.apache.sis.metadata.privy.ImplementationHelper.toDate; - import static org.apache.sis.metadata.privy.ImplementationHelper.toMilliseconds; + import org.apache.sis.util.privy.TemporalDate; +// Specific to the main branch: +import org.opengis.annotation.UML; +import static org.opengis.annotation.Obligation.OPTIONAL; +import static org.opengis.annotation.Specification.ISO_19115; + /** * Common ways in which the resource may be obtained or received, and related instructions @@@ -134,13 -129,11 +134,13 @@@ public class DefaultStandardOrderProces super(object); if (object != null) { fees = object.getFees(); - plannedAvailableDateTime = toMilliseconds(object.getPlannedAvailableDateTime()); + plannedAvailableDateTime = TemporalDate.toTemporal(object.getPlannedAvailableDateTime()); orderingInstructions = object.getOrderingInstructions(); turnaround = object.getTurnaround(); - orderOptionsType = object.getOrderOptionsType(); - orderOptions = object.getOrderOptions(); + if (object instanceof DefaultStandardOrderProcess) { + orderOptionsType = ((DefaultStandardOrderProcess) object).getOrderOptionsType(); + orderOptions = ((DefaultStandardOrderProcess) object).getOrderOptions(); + } } } diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/DefaultUsage.java index 32c7a33efc,fa65552a55..8c6ea0c521 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/DefaultUsage.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/DefaultUsage.java @@@ -163,15 -159,12 +164,15 @@@ public class DefaultUsage extends ISOMe super(object); if (object != null) { specificUsage = object.getSpecificUsage(); - usageDate = toMilliseconds(object.getUsageDate()); + usageDate = TemporalDate.toTemporal(object.getUsageDate()); userDeterminedLimitations = object.getUserDeterminedLimitations(); userContactInfo = copyCollection(object.getUserContactInfo(), ResponsibleParty.class); - responses = copyCollection(object.getResponses(), InternationalString.class); - additionalDocumentation = copyCollection(object.getAdditionalDocumentation(), Citation.class); - identifiedIssues = copyCollection(object.getIdentifiedIssues(), Citation.class); + if (object instanceof DefaultUsage) { + final DefaultUsage c = (DefaultUsage) object; + responses = copyCollection(c.getResponses(), InternationalString.class); + additionalDocumentation = copyCollection(c.getAdditionalDocumentation(), Citation.class); + identifiedIssues = copyCollection(c.getIdentifiedIssues(), Citation.class); + } } } diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/temporal/DefaultPeriod.java index ce43f62a4b,6f4cff5259..afc79c7459 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/temporal/DefaultPeriod.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/temporal/DefaultPeriod.java @@@ -17,10 -17,12 +17,10 @@@ package org.apache.sis.pending.temporal; import java.util.Objects; - import java.time.Instant; + import java.time.temporal.Temporal; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import java.time.Duration; -import java.time.temporal.TemporalAmount; -import org.opengis.temporal.Period; +// Specific to the main branch: +import org.apache.sis.pending.geoapi.temporal.Period; /** @@@ -29,12 -31,12 +29,12 @@@ * * @author Martin Desruisseaux (Geomatys) */ - final class DefaultPeriod implements Period { -public final class DefaultPeriod extends Primitive implements Period { ++public final class DefaultPeriod implements Period { /** Bounds making this period. */ - private final Instant beginning, ending; + private final Temporal beginning, ending; /** Creates a new period with the given bounds. */ - DefaultPeriod(final Instant beginning, final Instant ending) { + public DefaultPeriod(final Temporal beginning, final Temporal ending) { this.beginning = beginning; this.ending = ending; } diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataTest.java index 06fcd20859,a3a48f2b93..7505b27228 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataTest.java @@@ -154,9 -142,10 +153,9 @@@ public final class MetadataTest extend final var citation = new DefaultCitation("Some set of points"); citation.setAlternateTitles(Set.of(new SimpleInternationalString("Code XYZ"))); citation.setDates(List.of( - new DefaultCitationDate(TestUtilities.date("1990-06-04 22:00:00"), DateType.REVISION), - new DefaultCitationDate(TestUtilities.date("1979-08-02 22:00:00"), DateType.CREATION))); + new DefaultCitationDate(LocalDate.of(1990, 6, 5), DateType.REVISION), + new DefaultCitationDate(LocalDate.of(1979, 8, 3), DateType.CREATION))); { - @SuppressWarnings("deprecation") final var originator = new DefaultResponsibleParty(Role.ORIGINATOR); final var online = new DefaultOnlineResource(URI.create("http://www.com.univ-mrs.fr/LOB/")); online.setProtocol(Constants.HTTP); @@@ -222,10 -212,11 +221,10 @@@ final var aggregateInfo = new DefaultAggregateInformation(); final var name = new DefaultCitation("Some oceanographic campaign"); name.setAlternateTitles(Set.of(new SimpleInternationalString("Pseudo group of data"))); - name.setDates(Set.of(new DefaultCitationDate(TestUtilities.date("1990-06-04 22:00:00"), DateType.REVISION))); + name.setDates(Set.of(new DefaultCitationDate(LocalDate.of(1990, 6, 5), DateType.REVISION))); aggregateInfo.setName(name); aggregateInfo.setInitiativeType(InitiativeType.CAMPAIGN); - aggregateInfo.setAssociationType(AssociationType.LARGER_WORK_CITATION); + aggregateInfo.setAssociationType(AssociationType.LARGER_WORD_CITATION); // There is a typo ("WORD" → "WORK"), but we have to use the wrong spelling for this branch. identification.setAssociatedResources(Set.of(aggregateInfo)); } /* @@@ -380,11 -373,8 +380,9 @@@ "<gmx:Anchor xlink:href=\"SDN:L231:3:CDI\">Pseudo Common Data Index record</gmx:Anchor>"); replace(xml, "<gcol:CharacterString>4326</gcol:CharacterString>", "<gmx:Anchor xlink:href=\"SDN:L101:2:4326\">4326</gmx:Anchor>"); + replace(xml, "License", "Licence"); /* - * The <gmd:EX_TemporalExtent> block cannot be marshalled es expected yet (need a "sis-temporal" module). - * We need to instruct the XML comparator to ignore this block during the comparison. We also ignore for - * now the "gml:id" attribute since SIS generates different values than the ones in our test XML file, + * Ignore the "gml:id" attribute because SIS generates different values than the ones in our test XML file, * and those values may change in future SIS version. */ final var comparator = new DocumentComparator(getResource(), xml.toString()); diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataVerticalTest.java index d6701022c7,949b1c0a2d..56863a6c3b --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataVerticalTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataVerticalTest.java @@@ -87,10 -88,10 +89,10 @@@ public final class MetadataVerticalTes @Test public void testMetadataWithVerticalCRS() throws JAXBException { final Metadata metadata = unmarshalFile(Metadata.class, openTestFile()); - assertEquals("20090901", metadata.getFileIdentifier()); - assertEquals(Locale.ENGLISH, metadata.getLanguage()); - assertEquals(CharacterSet.UTF_8, metadata.getCharacterSet()); - assertEquals(xmlDate("2014-01-04 00:00:00"), metadata.getDateStamp()); - assertEquals("20090901", metadata.getMetadataIdentifier().getCode()); - assertEquals(Locale.ENGLISH, getSingleton(metadata.getLocalesAndCharsets().keySet())); - assertEquals(StandardCharsets.UTF_8, getSingleton(metadata.getLocalesAndCharsets().values())); - assertEquals(LocalDate.of(2014, 1, 4), TemporalDate.toTemporal(getSingleton(metadata.getDateInfo()).getDate())); ++ assertEquals("20090901", metadata.getFileIdentifier()); ++ assertEquals(Locale.ENGLISH, metadata.getLanguage()); ++ assertEquals(CharacterSet.UTF_8, metadata.getCharacterSet()); ++ assertEquals(LocalDate.of(2014, 1, 4), TemporalDate.toTemporal(metadata.getDateStamp())); /* * <gmd:contact> * <gmd:CI_ResponsibleParty> diff --cc endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/geoapi/temporal/Period.java index 9187bc9c74,a6eeffe0b8..f8ccb863ff --- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/geoapi/temporal/Period.java +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/geoapi/temporal/Period.java @@@ -14,31 -14,32 +14,31 @@@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.sis.xml.bind.referencing; +package org.apache.sis.pending.geoapi.temporal; - import java.time.Instant; -import org.opengis.referencing.datum.VerticalDatumType; -import org.apache.sis.xml.bind.gml.CodeListAdapter; ++import java.time.temporal.Temporal; +import org.opengis.temporal.TemporalPrimitive; /** - * JAXB adapter for (un)marshalling of GeoAPI code list. + * Placeholder for a GeoAPI interfaces not present in GeoAPI 3.0. * * @author Martin Desruisseaux (Geomatys) + * @since 0.3 + * @version 1.5 */ -@SuppressWarnings("deprecation") -public final class CD_VerticalDatumType extends CodeListAdapter<VerticalDatumType> { +public interface Period extends TemporalPrimitive { /** - * Empty constructor for JAXB only. + * Links this period to the instant at which it ends. + * + * @return The beginning instant. */ - Instant getBeginning(); - public CD_VerticalDatumType() { - } ++ Temporal getBeginning(); /** - * {@inheritDoc} + * Links this period to the instant at which it ends. * - * @return {@code VerticalDatumType.class} + * @return The end instant. */ - Instant getEnding(); - @Override - protected Class<VerticalDatumType> getCodeListClass() { - return VerticalDatumType.class; - } ++ Temporal getEnding(); } diff --cc optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/IdentificationInfo.java index 7a32422577,08a6632288..e5617a1c59 --- a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/IdentificationInfo.java +++ b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/IdentificationInfo.java @@@ -363,9 -364,9 +362,9 @@@ final class IdentificationInfo extends */ text = null; Identifier identifier = null; - Range<Instant> timeRange = null; + Range<Date> timeRange = null; Range<Double> heights = null; - for (final Extent extent : nonNull(info.getExtents())) { + for (final Extent extent : nonNull(dataInfo != null ? dataInfo.getExtents() : null)) { if (extent != null) { if (text == null) { text = owner.string(extent.getDescription());