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

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

commit f2f389fa68f4112a35733d85182b06951e0de3ca
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Fri Sep 9 19:32:58 2022 +0200

    Make the reference to source resource accessible throught lineage metadata.
---
 .../apache/sis/metadata/iso/extent/Extents.java    |  55 +++++-
 .../sis/metadata/iso/extent/package-info.java      |   2 +-
 .../internal/storage/DocumentedStoreProvider.java  |   2 +-
 .../sis/internal/storage/MetadataBuilder.java      |  23 ++-
 .../sis/internal/storage/ResourceLineage.java      | 198 +++++++++++++++++++++
 .../org/apache/sis/internal/storage/Resources.java |   5 +
 .../sis/internal/storage/Resources.properties      |   1 +
 .../sis/internal/storage/Resources_fr.properties   |   1 +
 .../sis/internal/storage/StoreUtilities.java       |  26 +++
 .../org/apache/sis/internal/storage/csv/Store.java |   2 +-
 .../org/apache/sis/storage/CoverageSubset.java     |  15 ++
 .../java/org/apache/sis/storage/FeatureSubset.java |  15 ++
 .../org/apache/sis/storage/CoverageQueryTest.java  |   8 +-
 .../org/apache/sis/storage/FeatureQueryTest.java   |   8 +-
 14 files changed, 352 insertions(+), 9 deletions(-)

diff --git 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
index ae145b88cc..9ed63cf613 100644
--- 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
+++ 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
@@ -17,9 +17,13 @@
 package org.apache.sis.metadata.iso.extent;
 
 import java.util.Date;
+import java.util.Locale;
 import java.util.List;
 import java.util.ArrayList;
-import java.util.Locale;
+import java.util.Set;
+import java.util.LinkedHashSet;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.function.Function;
 import java.util.function.BiConsumer;
 import javax.measure.Unit;
@@ -59,6 +63,7 @@ import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.Static;
 
 import static java.lang.Math.*;
+import static org.apache.sis.util.collection.Containers.isNullOrEmpty;
 import static org.apache.sis.internal.util.CollectionsExt.nonNull;
 import static 
org.apache.sis.internal.metadata.ReferencingServices.AUTHALIC_RADIUS;
 
@@ -79,7 +84,7 @@ import org.opengis.geometry.Geometry;
  * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.3
  *
  * @see org.apache.sis.geometry.Envelopes
  *
@@ -118,6 +123,52 @@ public final class Extents extends Static {
         WORLD = world;
     }
 
+    /**
+     * Returns the extents found in all {@code Identification} elements of the 
given metadata.
+     * If there is only one {@link Identification} element (which is the usual 
case), then its
+     * collection of extents is returned <em>as-is</em>; the collection is not 
copied.
+     *
+     * <p>In the less usual case where there is many {@link Identification} 
elements providing
+     * non-empty collection of extents, then this method returns the union of 
all collections
+     * without duplicated elements (duplication determined by {@link 
Object#equals(Object)}).
+     * In the special case where the first non-empty collection of extents 
contains all other
+     * collections, then that collection is returned <em>as-is</em>.</p>
+     *
+     * <div class="note"><b>Rational:</b>
+     * above policy makes a best effort for avoiding to create new collections.
+     * The reason is that collection implementations may perform lazy 
calculations of {@link Extent} elements.
+     * This method tries to preserve the lazy behavior (if any).</div>
+     *
+     * @param  metadata  the metadata, or {@code null} if none.
+     * @return extents found in all {@link Identification} elements, or an 
empty collection if none.
+     *
+     * @since 1.3
+     */
+    public static Collection<? extends Extent> fromIdentificationInfo(final 
Metadata metadata) {
+        Collection<? extends Extent> result = null;
+        if (metadata != null) {
+            Set<Extent> union = null;
+            for (final Identification id : 
nonNull(metadata.getIdentificationInfo())) {
+                if (id != null) {       // Should not be allowed, but we are 
paranoiac.
+                    final Collection<? extends Extent> extents = 
id.getExtents();
+                    if (extents != result && !isNullOrEmpty(extents)) {
+                        if (result == null) {
+                            result = extents;
+                        } else {
+                            if (union == null) {
+                                union = new LinkedHashSet<>(result);
+                            }
+                            if (union.addAll(extents)) {
+                                result = union;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return (result != null) ? result : Collections.emptyList();
+    }
+
     /**
      * Returns a single geographic bounding box from the specified metadata. 
If the given metadata
      * contains many {@link Identification} or many {@link Extent} instances, 
then this method returns
diff --git 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/package-info.java
 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/package-info.java
index d5d88c6871..3f83535df4 100644
--- 
a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/package-info.java
+++ 
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/package-info.java
@@ -136,7 +136,7 @@
  * @author  Cédric Briançon (Geomatys)
  * @author  Guilhem Legal (Geomatys)
  * @author  Cullen Rombach (Image Matters)
- * @version 1.0
+ * @version 1.3
  * @since   0.3
  * @module
  */
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/DocumentedStoreProvider.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/DocumentedStoreProvider.java
index 7f653ac1fc..319760f61e 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/DocumentedStoreProvider.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/DocumentedStoreProvider.java
@@ -27,7 +27,7 @@ import org.apache.sis.internal.system.Modules;
 
 
 /**
- * Base class of data store provider having an entry in the metadata SQL 
database.
+ * Base class of data store providers having an entry in the metadata SQL 
database.
  * The primary key in the {@code MD_Format} table must be the name given at 
construction time.
  *
  * @author  Martin Desruisseaux (Geomatys)
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 dac07899b8..7ef829f5e6 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
@@ -73,6 +73,7 @@ import org.apache.sis.metadata.iso.maintenance.*;
 import org.apache.sis.metadata.iso.spatial.*;
 import org.apache.sis.metadata.sql.MetadataStoreException;
 import org.apache.sis.metadata.sql.MetadataSource;
+import org.apache.sis.storage.Resource;
 import org.apache.sis.storage.AbstractResource;
 import org.apache.sis.storage.AbstractFeatureSet;
 import org.apache.sis.storage.AbstractGridCoverageResource;
@@ -110,7 +111,7 @@ import org.opengis.feature.FeatureType;
  * @author  Rémi Maréchal (Geomatys)
  * @author  Thi Phuong Hao Nguyen (VNSC)
  * @author  Alexis Manin (Geomatys)
- * @version 1.2
+ * @version 1.3
  * @since   0.8
  * @module
  */
@@ -1081,7 +1082,8 @@ public class MetadataBuilder {
 
     /**
      * Adds information about the scope of the resource.
-     * The scope is typically {@link ScopeCode#DATASET}.
+     * The scope is typically (but not restricted to) {@link 
ScopeCode#COVERAGE},
+     * {@link ScopeCode#FEATURE} or the more generic {@link ScopeCode#DATASET}.
      * Storage locations are:
      *
      * <ul>
@@ -2892,6 +2894,23 @@ parse:      for (int i = 0; i < length;) {
         }
     }
 
+    /**
+     * Creates metadata about the sources of a resource.
+     * Storage location is:
+     *
+     * <ul>
+     *   <li>{@code metadata/resourceLineage/source}</li>
+     * </ul>
+     *
+     * @param  sources  the sources of the resource for which to describe the 
lineage.
+     * @throws DataStoreException if an error occurred while fetching metadata 
from a resource.
+     */
+    public final void addSources(final Resource... sources) throws 
DataStoreException {
+        if (sources != null && sources.length != 0) {
+            ResourceLineage.setSources(lineage(), sources);
+        }
+    }
+
     /**
      * Adds information about a source of data used for producing the resource.
      * Storage location is:
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ResourceLineage.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ResourceLineage.java
new file mode 100644
index 0000000000..3c7f5a784e
--- /dev/null
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ResourceLineage.java
@@ -0,0 +1,198 @@
+/*
+ * 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.storage;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.opengis.metadata.Metadata;
+import org.opengis.metadata.MetadataScope;
+import org.opengis.metadata.extent.Extent;
+import org.opengis.metadata.lineage.Source;
+import org.opengis.metadata.maintenance.Scope;
+import org.opengis.metadata.maintenance.ScopeCode;
+import org.opengis.metadata.citation.Citation;
+import org.opengis.metadata.identification.Resolution;
+import org.opengis.metadata.identification.Identification;
+import org.opengis.referencing.ReferenceSystem;
+import org.opengis.util.InternationalString;
+import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.metadata.iso.extent.Extents;
+import org.apache.sis.metadata.iso.lineage.DefaultLineage;
+import org.apache.sis.metadata.iso.maintenance.DefaultScope;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.Resource;
+
+import static org.apache.sis.internal.util.CollectionsExt.nonNull;
+
+
+/**
+ * Metadata about a resource which is a single source of another resource.
+ * This is an experimental class which may be revisited in any future version.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.3
+ * @since   1.3
+ * @module
+ */
+final class ResourceLineage implements Source {
+    /**
+     * The source of the derived resource described by the lineage.
+     */
+    public final Resource source;
+
+    /**
+     * Metadata of the source, or {@code null} if none.
+     * All properties returned by this class are inferred from those metadata.
+     */
+    private final Metadata metadata;
+
+    /**
+     * Creates a new source wrapping the given resource.
+     *
+     * @param  source  the source of the derived resource described by the 
resource lineage.
+     * @throws DataStoreException if an error occurred while fetching metadata 
from the source.
+     */
+    private ResourceLineage(final Resource source) throws DataStoreException {
+        this.source = source;
+        metadata = source.getMetadata();
+    }
+
+    /**
+     * Writes metadata about the sources of a resource.
+     *
+     * @param  lineage  where to write lineage information.
+     * @param  sources  the sources of the resource for which to describe the 
lineage.
+     * @throws DataStoreException if an error occurred while fetching metadata 
from a resource.
+     */
+    static void setSources(final DefaultLineage lineage, final Resource... 
sources) throws DataStoreException {
+        final ResourceLineage[] wrappers  = new 
ResourceLineage[sources.length];
+        for (int i=0; i<wrappers.length; i++) {
+            wrappers[i] = new ResourceLineage(sources[i]);
+        }
+        lineage.setSources(Arrays.asList(wrappers));
+    }
+
+    /**
+     * Returns a description of the level of the source data.
+     * Default implementation returns the title of the {@linkplain 
#getSourceCitation() source citation}.
+     *
+     * @return description of the level of the source data, or {@code null} if 
none.
+     */
+    @Override
+    public InternationalString getDescription() {
+        final Citation citation = getSourceCitation();
+        return (citation != null) ? citation.getTitle() : null;
+    }
+
+    /**
+     * Returns the recommended reference to be used for the source data.
+     * Default implementation returns the first citation having a non-null 
title
+     * among the citations provided by {@link 
Metadata#getIdentificationInfo()}.
+     *
+     * @return recommended reference to be used for the source data, or {@code 
null}.
+     */
+    @Override
+    public Citation getSourceCitation() {
+        if (metadata != null) {
+            for (final Identification info : 
nonNull(metadata.getIdentificationInfo())) {
+                final Citation citation = info.getCitation();
+                if (citation != null) {
+                    if (citation.getTitle() != null) {
+                        return citation;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the type and extent of the source. Default implementation 
returns the resource scope
+     * declared in source metadata, together with the {@linkplain 
#getSourceExtents() source extents}.
+     *
+     * @return type and extent of the source, or {@code null} if none.
+     */
+    @Override
+    public Scope getScope() {
+        return createScope(metadata);
+    }
+
+    /**
+     * Creates a scope from the given resource metadata.
+     */
+    private static Scope createScope(final Metadata metadata) {
+        if (metadata != null) {
+            ScopeCode level = null;
+            for (final MetadataScope ms : 
nonNull(metadata.getMetadataScopes())) {
+                level = ms.getResourceScope();
+                if (level != null) break;
+            }
+            final Collection<? extends Extent> extents = 
Extents.fromIdentificationInfo(metadata);
+            if (level != null || !extents.isEmpty()) {
+                final DefaultScope scope = new DefaultScope(level);
+                scope.setExtents(extents);
+                return scope;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Information about the spatial, vertical and temporal extent of the 
source data.
+     * Default implementation returns all extents declared in {@link 
Metadata#getIdentificationInfo()}.
+     *
+     * @return information about the extent of the source data.
+     *
+     * @deprecated As of ISO 19115:2014, moved to {@link Scope#getExtents()}.
+     */
+    @Override
+    @Deprecated
+    public Collection<? extends Extent> getSourceExtents() {
+        return Extents.fromIdentificationInfo(metadata);
+    }
+
+    /**
+     * Returns the spatial reference system used by the source data.
+     * Default implementation returns the first reference system declared by 
metadata.
+     *
+     * @return spatial reference system used by the source data, or {@code 
null}.
+     */
+    @Override
+    public ReferenceSystem getSourceReferenceSystem() {
+        return (metadata != null) ? 
CollectionsExt.first(metadata.getReferenceSystemInfo()) : null;
+    }
+
+    /**
+     * Spatial resolution expressed as a scale factor, an angle or a level of 
detail.
+     * Default implementation returns the first resolution found in 
identification information.
+     *
+     * @return spatial resolution, or {@code null} if none.
+     */
+    @Override
+    public Resolution getSourceSpatialResolution() {
+        if (metadata != null) {
+            for (final Identification info : 
nonNull(metadata.getIdentificationInfo())) {
+                for (final Resolution candidate : 
nonNull(info.getSpatialResolutions())) {
+                    if (candidate != null) {
+                        return candidate;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java
index a4fe0b30c9..292e4a0920 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java
@@ -336,6 +336,11 @@ public final class Resources extends IndexedResourceBundle 
{
          */
         public static final short NotAWritableFeatureSet_1 = 47;
 
+        /**
+         * Original data.
+         */
+        public static final short OriginalData = 76;
+
         /**
          * Processing executed on {0}.
          */
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties
index ad995bb769..ce57d3a1cc 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties
@@ -74,6 +74,7 @@ NoCommonFeatureType               = No feature type is common 
to all the feature
 NoSuchResourceDirectory_1         = No directory of resources found at 
\u201c{0}\u201d.
 NoSuchResourceInAggregate_2       = Resource \u201c{1}\u201d is not part of 
aggregate \u201c{0}\u201d.
 NotAWritableFeatureSet_1          = Resource \u201c{0}\u201d is not a writable 
feature set.
+OriginalData                      = Original data.
 ProcessingExecutedOn_1            = Processing executed on {0}.
 ResourceAlreadyExists_1           = A resource already exists at 
\u201c{0}\u201d.
 ResourceIdentifierCollision_2     = More than one resource have the 
\u201c{1}\u201d identifier in the \u201c{0}\u201d data store.
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties
index bae8560190..a2dc7693cb 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties
@@ -79,6 +79,7 @@ NoCommonFeatureType               = Il n\u2019y a pas de type 
commun \u00e0 tout
 NoSuchResourceDirectory_1         = Aucun r\u00e9pertoire de ressources 
n\u2019a \u00e9t\u00e9 trouv\u00e9 \u00e0 l\u2019emplacement 
\u00ab\u202f{0}\u202f\u00bb.
 NoSuchResourceInAggregate_2       = La ressource \u00ab\u202f{1}\u202f\u00bb 
n\u2019est pas une partie de l\u2019agr\u00e9gat \u00ab\u202f{0}\u202f\u00bb.
 NotAWritableFeatureSet_1          = La ressource \u00ab\u202f{0}\u202f\u00bb 
n\u2019est pas un ensemble d\u2019entit\u00e9s accessibles en \u00e9criture.
+OriginalData                      = Donn\u00e9es originales.
 ProcessingExecutedOn_1            = Traitement ex\u00e9cut\u00e9 sur {0}.
 ResourceAlreadyExists_1           = Une ressource existe d\u00e9j\u00e0 \u00e0 
l\u2019emplacement \u00ab\u202f{0}\u202f\u00bb.
 ResourceIdentifierCollision_2     = Plusieurs ressources utilisent 
l\u2019identifiant \u00ab\u202f{1}\u202f\u00bb dans les donn\u00e9es de 
\u00ab\u202f{0}\u202f\u00bb.
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreUtilities.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreUtilities.java
index eaeb355bab..e9b9586c91 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreUtilities.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreUtilities.java
@@ -18,6 +18,8 @@ package org.apache.sis.internal.storage;
 
 import java.util.Set;
 import java.util.EnumSet;
+import java.util.List;
+import java.util.ArrayList;
 import java.util.Optional;
 import java.util.stream.Stream;
 import java.util.logging.Filter;
@@ -34,6 +36,8 @@ import org.opengis.metadata.extent.GeographicExtent;
 import org.opengis.metadata.extent.GeographicBoundingBox;
 import org.opengis.metadata.identification.Identification;
 import org.opengis.metadata.identification.DataIdentification;
+import org.opengis.metadata.lineage.Lineage;
+import org.opengis.metadata.lineage.Source;
 import org.apache.sis.util.Static;
 import org.apache.sis.storage.FeatureSet;
 import org.apache.sis.storage.Resource;
@@ -52,6 +56,8 @@ import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Classes;
 
+import static org.apache.sis.internal.util.CollectionsExt.nonNull;
+
 // Branch-dependent imports
 import org.opengis.feature.Feature;
 
@@ -208,6 +214,26 @@ public final class StoreUtilities extends Static {
         return bounds;
     }
 
+    /**
+     * Fetches all resources that are the sources of a resource described by 
given metadata.
+     *
+     * @param  metadata  metadata of the resource for which to get the 
sources, or {@code null} if none.
+     * @return sources found in given metadata.
+     */
+    public static List<Resource> getSources(final Metadata metadata) {
+        final List<Resource> sources = new ArrayList<>();
+        if (metadata != null) {
+            for (final Lineage lineage : 
nonNull(metadata.getResourceLineages())) {
+                for (final Source candidate : nonNull(lineage.getSources())) {
+                    if (candidate instanceof ResourceLineage) {
+                        sources.add(((ResourceLineage) candidate).source);
+                    }
+                }
+            }
+        }
+        return sources;
+    }
+
     /**
      * Returns the most specific interface implemented by the given class.
      * For indicative purpose only, as this method has arbitrary behavior if 
more than one leaf is found.
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
index adb49da93a..9e6cba17f4 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
@@ -642,7 +642,7 @@ final class Store extends URIDataStore implements 
FeatureSet {
                 listeners.warning(e);
             }
             builder.addEncoding(encoding, MetadataBuilder.Scope.ALL);
-            builder.addResourceScope(ScopeCode.DATASET, null);
+            builder.addResourceScope(ScopeCode.FEATURE, null);
             try {
                 builder.addExtent(envelope);
             } catch (TransformException e) {
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageSubset.java 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageSubset.java
index 0a68a344e8..447a81ca27 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageSubset.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageSubset.java
@@ -18,6 +18,7 @@ package org.apache.sis.storage;
 
 import java.util.Arrays;
 import java.util.List;
+import org.opengis.metadata.Metadata;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.coverage.SampleDimension;
@@ -28,6 +29,7 @@ import org.apache.sis.coverage.grid.GridRoundingMode;
 import org.apache.sis.coverage.grid.GridClippingMode;
 import org.apache.sis.coverage.grid.DisjointExtentException;
 import org.apache.sis.internal.storage.Resources;
+import org.apache.sis.internal.storage.MetadataBuilder;
 import org.apache.sis.internal.util.UnmodifiableArrayList;
 
 
@@ -65,6 +67,19 @@ final class CoverageSubset extends 
AbstractGridCoverageResource {
         this.query  = query;
     }
 
+    /**
+     * Creates metadata about this subset.
+     * It includes information about the complete feature set.
+     */
+    @Override
+    protected Metadata createMetadata() throws DataStoreException {
+        final MetadataBuilder builder = new MetadataBuilder();
+        builder.addDefaultMetadata(this, listeners);
+        
builder.addLineage(Resources.formatInternational(Resources.Keys.OriginalData));
+        builder.addSources(source);
+        return builder.build();
+    }
+
     /**
      * Returns the valid extent of grid coordinates clipped to the area 
specified in the query.
      * It should be the geometry of the coverage that we get when invoking 
{@link #read read(…)}
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSubset.java 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSubset.java
index 4affc9612e..c1e5d01d94 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSubset.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSubset.java
@@ -18,7 +18,9 @@ package org.apache.sis.storage;
 
 import java.util.OptionalLong;
 import java.util.stream.Stream;
+import org.opengis.metadata.Metadata;
 import org.apache.sis.internal.feature.FeatureUtilities;
+import org.apache.sis.internal.storage.MetadataBuilder;
 import org.apache.sis.internal.storage.Resources;
 
 // Branch-dependent imports
@@ -68,6 +70,19 @@ final class FeatureSubset extends AbstractFeatureSet {
         this.query = query;
     }
 
+    /**
+     * Creates metadata about this subset.
+     * It includes information about the complete feature set.
+     */
+    @Override
+    protected Metadata createMetadata() throws DataStoreException {
+        final MetadataBuilder builder = new MetadataBuilder();
+        builder.addDefaultMetadata(this, listeners);
+        
builder.addLineage(Resources.formatInternational(Resources.Keys.OriginalData));
+        builder.addSources(source);
+        return builder.build();
+    }
+
     /**
      * Returns a description of properties that are common to all features in 
this dataset.
      */
diff --git 
a/storage/sis-storage/src/test/java/org/apache/sis/storage/CoverageQueryTest.java
 
b/storage/sis-storage/src/test/java/org/apache/sis/storage/CoverageQueryTest.java
index 305908c2a8..ebe47db3cc 100644
--- 
a/storage/sis-storage/src/test/java/org/apache/sis/storage/CoverageQueryTest.java
+++ 
b/storage/sis-storage/src/test/java/org/apache/sis/storage/CoverageQueryTest.java
@@ -23,18 +23,20 @@ import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
+import org.apache.sis.internal.storage.StoreUtilities;
 import org.apache.sis.referencing.crs.HardCodedCRS;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
+import static org.apache.sis.test.TestUtilities.getSingleton;
 
 
 /**
  * Tests {@link CoverageQuery} and (indirectly) {@link CoverageSubset}.
  *
  * @author  Johann Sorel (Geomatys)
- * @version 1.1
+ * @version 1.3
  * @since   1.1
  */
 public final strictfp class CoverageQueryTest extends TestCase {
@@ -178,5 +180,9 @@ public final strictfp class CoverageQueryTest extends 
TestCase {
             request = createSubGrid(-4 + expansion);
         }
         assertEquals(request, coverage.getGridGeometry());
+        /*
+         * Verify lineage metadata. They are stored in specialized sub-type of 
`Source`.
+         */
+        assertSame(resource, 
getSingleton(StoreUtilities.getSources(subset.getMetadata())));
     }
 }
diff --git 
a/storage/sis-storage/src/test/java/org/apache/sis/storage/FeatureQueryTest.java
 
b/storage/sis-storage/src/test/java/org/apache/sis/storage/FeatureQueryTest.java
index a4d157da7a..3e8313826a 100644
--- 
a/storage/sis-storage/src/test/java/org/apache/sis/storage/FeatureQueryTest.java
+++ 
b/storage/sis-storage/src/test/java/org/apache/sis/storage/FeatureQueryTest.java
@@ -22,12 +22,14 @@ import java.util.Iterator;
 import java.util.stream.Collectors;
 import org.apache.sis.feature.builder.FeatureTypeBuilder;
 import org.apache.sis.internal.storage.MemoryFeatureSet;
+import org.apache.sis.internal.storage.StoreUtilities;
 import org.apache.sis.filter.DefaultFilterFactory;
 import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
+import static org.apache.sis.test.TestUtilities.getSingleton;
 
 // Branch-dependent imports
 import org.opengis.feature.Feature;
@@ -47,7 +49,7 @@ import org.opengis.filter.SortProperty;
  * @author  Johann Sorel (Geomatys)
  * @author  Alexis Manin (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.3
  * @since   1.0
  * @module
  */
@@ -141,6 +143,10 @@ public final strictfp class FeatureQueryTest extends 
TestCase {
                                  + "Actual:%n%s%n", i, expected, actual));
             }
         }
+        /*
+         * Verify lineage metadata. They are stored in specialized sub-type of 
`Source`.
+         */
+        assertSame(featureSet, 
getSingleton(StoreUtilities.getSources(fs.getMetadata())));
     }
 
     /**

Reply via email to