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 aab36b9ac3a40f9e0a9e80ff7267b865e2d088cb
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Thu Mar 24 14:39:25 2022 +0100

    Avoid direct reference to `MetadataBuilder` internal class in 
`AbstractResource` API.
    This is a step (together with previous commit) toward moving 
`AbstractResource` to public API.
---
 .../org/apache/sis/storage/landsat/BandGroup.java  |  11 +-
 .../org/apache/sis/internal/netcdf/FeatureSet.java |   2 +-
 .../apache/sis/internal/netcdf/RasterResource.java |  10 +-
 .../sis/internal/storage/AbstractFeatureSet.java   |  24 +++--
 .../sis/internal/storage/AbstractGridResource.java |  24 +++--
 .../sis/internal/storage/AbstractResource.java     | 118 +++++++--------------
 .../sis/internal/storage/AggregatedFeatureSet.java |   7 +-
 .../internal/storage/ConcatenatedFeatureSet.java   |   4 +-
 .../sis/internal/storage/MemoryFeatureSet.java     |   4 +-
 .../sis/internal/storage/MetadataBuilder.java      |  82 ++++++++++++++
 10 files changed, 169 insertions(+), 117 deletions(-)

diff --git 
a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/BandGroup.java
 
b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/BandGroup.java
index b16b78d..3750f28 100644
--- 
a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/BandGroup.java
+++ 
b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/BandGroup.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Optional;
 import org.opengis.util.GenericName;
+import org.opengis.metadata.Metadata;
 import org.apache.sis.storage.Resource;
 import org.apache.sis.storage.Aggregate;
 import org.apache.sis.storage.DataStoreException;
@@ -41,7 +42,7 @@ import org.apache.sis.util.ArraysExt;
  *       a sample dimension.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
  * @since   1.1
  * @module
  */
@@ -110,9 +111,11 @@ final class BandGroup extends AbstractResource implements 
Aggregate {
      * Invoked in a synchronized block the first time that {@link 
#getMetadata()} is invoked.
      */
     @Override
-    protected void createMetadata(final MetadataBuilder metadata) throws 
DataStoreException {
-        metadata.addTitle(group.title);     // Must be before 
`super.createMetadata(…)`.
-        super.createMetadata(metadata);
+    protected Metadata createMetadata() throws DataStoreException {
+        final MetadataBuilder metadata = new MetadataBuilder();
+        metadata.addTitle(group.title);             // Must be before 
`addDefaultMetadata(…)`.
+        metadata.addDefaultMetadata(this, this);
+        return metadata.build(true);
     }
 
     /**
diff --git 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/FeatureSet.java
 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/FeatureSet.java
index 666ffcb..aab4f13 100644
--- 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/FeatureSet.java
+++ 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/FeatureSet.java
@@ -588,7 +588,7 @@ skip:           for (final Variable v : properties) {
      * @return the number of features.
      */
     @Override
-    protected OptionalLong getFeatureCount() {
+    public OptionalLong getFeatureCount() {
         if (counts != null) {
             return OptionalLong.of(counts.size());
         }
diff --git 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
index 9d615ae..db1ece4 100644
--- 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
+++ 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
@@ -28,6 +28,7 @@ import java.nio.Buffer;
 import java.awt.image.DataBuffer;
 
 import org.opengis.util.GenericName;
+import org.opengis.metadata.Metadata;
 import org.opengis.referencing.operation.MathTransform1D;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.internal.storage.AbstractGridResource;
@@ -387,11 +388,11 @@ public final class RasterResource extends 
AbstractGridResource implements Resour
      * provided by {@link #getIdentifier()}, {@link #getGridGeometry()}, 
{@link #getSampleDimensions()} and
      * variable names.
      *
-     * @param  metadata  the builder where to set metadata properties.
-     * @throws DataStoreException if an error occurred while reading metadata 
from the data store.
+     * @throws DataStoreException if an error occurred while reading metadata 
from this resource.
      */
     @Override
-    protected void createMetadata(final MetadataBuilder metadata) throws 
DataStoreException {
+    protected Metadata createMetadata() throws DataStoreException {
+        final MetadataBuilder metadata = new MetadataBuilder();
         String title = null;
         for (final Variable v : data) {
             title = (String) CharSequences.commonWords(title, 
v.getDescription());
@@ -400,7 +401,8 @@ public final class RasterResource extends 
AbstractGridResource implements Resour
         if (title != null && !title.isEmpty()) {
             
metadata.addTitle(CharSequences.camelCaseToSentence(title).toString());
         }
-        super.createMetadata(metadata);
+        metadata.addDefaultMetadata(this, this);
+        return metadata.build(true);
     }
 
     /**
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractFeatureSet.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractFeatureSet.java
index 07da846..6062a9c 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractFeatureSet.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractFeatureSet.java
@@ -19,6 +19,7 @@ package org.apache.sis.internal.storage;
 import java.util.Optional;
 import java.util.OptionalLong;
 import org.opengis.util.GenericName;
+import org.opengis.metadata.Metadata;
 import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.FeatureSet;
@@ -46,7 +47,7 @@ import org.opengis.feature.FeatureType;
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.2
  * @since   0.8
  * @module
  */
@@ -83,21 +84,26 @@ public abstract class AbstractFeatureSet extends 
AbstractResource implements Fea
      *
      * @return estimation of the number of features.
      */
-    protected OptionalLong getFeatureCount() {
+    public OptionalLong getFeatureCount() {
         return OptionalLong.empty();
     }
 
     /**
-     * Invoked the first time that {@link #getMetadata()} is invoked. The 
default implementation populates metadata
-     * based on information provided by {@link #getType()}, {@link 
#getIdentifier()} and {@link #getEnvelope()}.
+     * Invoked in a synchronized block the first time that {@code 
getMetadata()} is invoked.
+     * The default implementation populates metadata based on information 
provided by
+     * {@link #getIdentifier()   getIdentifier()},
+     * {@link #getEnvelope()     getEnvelope()},
+     * {@link #getType()         getType()} and
+     * {@link #getFeatureCount() getFeatureCount()}.
      * Subclasses should override if they can provide more information.
      *
-     * @param  metadata  the builder where to set metadata properties.
-     * @throws DataStoreException if an error occurred while reading metadata 
from the data store.
+     * @return the newly created metadata, or {@code null} if unknown.
+     * @throws DataStoreException if an error occurred while reading metadata 
from this resource.
      */
     @Override
-    protected void createMetadata(final MetadataBuilder metadata) throws 
DataStoreException {
-        super.createMetadata(metadata);
-        metadata.addFeatureType(getType(), getFeatureCount().orElse(-1));
+    protected Metadata createMetadata() throws DataStoreException {
+        final MetadataBuilder builder = new MetadataBuilder();
+        builder.addDefaultMetadata(this, this);
+        return builder.build(true);
     }
 }
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java
index 6493a50..0aaf6c3 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java
@@ -29,6 +29,7 @@ import java.awt.image.ColorModel;
 import java.awt.image.SampleModel;
 import java.awt.image.RasterFormatException;
 import org.opengis.geometry.Envelope;
+import org.opengis.metadata.Metadata;
 import org.opengis.metadata.spatial.DimensionNameType;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.util.FactoryException;
@@ -109,20 +110,21 @@ public abstract class AbstractGridResource extends 
AbstractResource implements G
     }
 
     /**
-     * Invoked the first time that {@link #getMetadata()} is invoked. The 
default implementation populates
-     * metadata based on information provided by {@link #getIdentifier()}, 
{@link #getGridGeometry()} and
-     * {@link #getSampleDimensions()}. Subclasses should override if they can 
provide more information.
+     * Invoked in a synchronized block the first time that {@code 
getMetadata()} is invoked.
+     * The default implementation populates metadata based on information 
provided by
+     * {@link #getIdentifier()       getIdentifier()},
+     * {@link #getGridGeometry()     getGridGeometry()} and
+     * {@link #getSampleDimensions() getSampleDimensions()}.
+     * Subclasses should override if they can provide more information.
      *
-     * @param  metadata  the builder where to set metadata properties.
-     * @throws DataStoreException if an error occurred while reading metadata 
from the data store.
+     * @return the newly created metadata, or {@code null} if unknown.
+     * @throws DataStoreException if an error occurred while reading metadata 
from this resource.
      */
     @Override
-    protected void createMetadata(final MetadataBuilder metadata) throws 
DataStoreException {
-        super.createMetadata(metadata);
-        metadata.addSpatialRepresentation(null, getGridGeometry(), false);
-        for (final SampleDimension band : getSampleDimensions()) {
-            metadata.addNewBand(band);
-        }
+    protected Metadata createMetadata() throws DataStoreException {
+        final MetadataBuilder builder = new MetadataBuilder();
+        builder.addDefaultMetadata(this, this);
+        return builder.build(true);
     }
 
     /**
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java
index 8a9c984..a16efba 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java
@@ -16,10 +16,8 @@
  */
 package org.apache.sis.internal.storage;
 
-import java.util.Locale;
 import java.util.Optional;
 import org.opengis.util.GenericName;
-import org.opengis.util.InternationalString;
 import org.opengis.metadata.Metadata;
 import org.opengis.geometry.Envelope;
 import org.opengis.referencing.operation.TransformException;
@@ -33,20 +31,20 @@ import org.apache.sis.storage.event.StoreEvent;
 import org.apache.sis.storage.event.StoreListener;
 import org.apache.sis.storage.event.StoreListeners;
 import org.apache.sis.storage.event.WarningEvent;
-import org.apache.sis.util.AbstractInternationalString;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.logging.Logging;
-import org.apache.sis.util.CharSequences;
+import org.apache.sis.xml.NilReason;
 
 
 /**
  * Base implementation of resources contained in data stores. This class 
provides a {@link #getMetadata()}
- * which extracts information from other methods. Subclasses shall or should 
override the following methods:
+ * method which extracts information from other methods. Subclasses should 
override the following methods:
  *
  * <ul>
- *   <li>{@link #getIdentifier()} (mandatory)</li>
+ *   <li>{@link #getIdentifier()} (strongly recommended)</li>
  *   <li>{@link #getEnvelope()} (recommended)</li>
- *   <li>{@link #createMetadata(MetadataBuilder)} (optional)</li>
+ *   <li>{@link #createMetadata()} (optional)</li>
+ *   <li>{@link #getSynchronizationLock()} (optional)</li>
  * </ul>
  *
  * This class extends {@link StoreListeners} for convenience reasons.
@@ -64,10 +62,10 @@ import org.apache.sis.util.CharSequences;
 public class AbstractResource extends StoreListeners implements Resource {
     /**
      * A description of this resource as an unmodifiable metadata, or {@code 
null} if not yet computed.
-     * If non-null, this metadata shall contain at least the resource 
{@linkplain #getIdentifier() identifier}.
+     * If non-null, this metadata should contain at least the resource 
{@linkplain #getIdentifier() identifier}.
      * Those metadata are created by {@link #getMetadata()} when first needed.
      */
-    private Metadata metadata;
+    private volatile Metadata metadata;
 
     /**
      * Creates a new resource. This resource will have its own set of 
listeners,
@@ -86,11 +84,10 @@ public class AbstractResource extends StoreListeners 
implements Resource {
      * The default implementation returns an empty value.
      * Subclasses are strongly encouraged to override if they can provide a 
value.
      *
-     * <p>Note that the default implementation of {@link 
#createMetadata(MetadataBuilder)} uses this identifier
-     * for initializing the {@code metadata/identificationInfo/citation/title} 
property. So it is generally not
-     * useful to fallback on metadata if the identifier is empty.</p>
-     *
-     * @see org.apache.sis.internal.storage.StoreUtilities#getLabel(Resource)
+     * <div class="note"><b>Implementation note:</b>
+     * the default implementation of {@link #createMetadata()} uses this 
identifier for initializing
+     * the {@code metadata/identificationInfo/citation/title} property. So it 
is generally not useful
+     * to fallback on metadata if the identifier is empty.</div>
      */
     @Override
     public Optional<GenericName> getIdentifier() throws DataStoreException {
@@ -100,7 +97,7 @@ public class AbstractResource extends StoreListeners 
implements Resource {
     /**
      * Returns the spatiotemporal envelope of this resource. This information 
is part of API only in some kinds of resource
      * like {@link org.apache.sis.storage.FeatureSet}. But the method is 
provided in this base class for convenience and for
-     * allowing {@link #getMetadata()} to use this information if available. 
The default implementation gives an empty value.
+     * allowing {@link #createMetadata()} to use this information if 
available. The default implementation gives an empty value.
      *
      * @return the spatiotemporal resource extent.
      * @throws DataStoreException if an error occurred while reading or 
computing the envelope.
@@ -110,95 +107,52 @@ public class AbstractResource extends StoreListeners 
implements Resource {
     }
 
     /**
-     * Returns a description of this resource. This method invokes {@link 
#createMetadata(MetadataBuilder)}
-     * the first time it is invoked, then caches the result.
+     * Returns a description of this resource. This method invokes {@link 
#createMetadata()}
+     * in a synchronized block when first needed, then caches the result.
      *
      * @return information about this resource (never {@code null} in this 
implementation).
-     * @throws DataStoreException if an error occurred while reading or 
computing the envelope.
+     * @throws DataStoreException if an error occurred while reading or 
computing the metadata.
      */
     @Override
     public final Metadata getMetadata() throws DataStoreException {
-        synchronized (getSynchronizationLock()) {
-            if (metadata == null) {
-                metadata = createMetadata();
+        Metadata md = metadata;
+        if (md == null) {
+            synchronized (getSynchronizationLock()) {
+                md = metadata;
+                if (md == null) {
+                    md = createMetadata();
+                    if (md == null) {
+                        md = NilReason.UNKNOWN.createNilObject(Metadata.class);
+                    }
+                    metadata = md;
+                }
             }
-            return metadata;
         }
+        return md;
     }
 
     /**
      * Invoked in a synchronized block the first time that {@link 
#getMetadata()} is invoked.
-     * The default implementation delegates to {@link 
#createMetadata(MetadataBuilder)}.
-     * Subclasses can override if they want to use a different kind of builder.
-     *
-     * @return the newly created metadata.
-     * @throws DataStoreException if an error occurred while reading metadata 
from the data store.
-     */
-    protected Metadata createMetadata() throws DataStoreException {
-        final MetadataBuilder builder = new MetadataBuilder();
-        createMetadata(builder);
-        return builder.build(true);
-    }
-
-    /**
-     * Invoked by the default implementation of {@link #createMetadata()}.
      * The default implementation populates metadata based on information
      * provided by {@link #getIdentifier()} and {@link #getEnvelope()}.
      * Subclasses should override if they can provide more information.
      *
-     * @param  metadata  the builder where to set metadata properties.
-     * @throws DataStoreException if an error occurred while reading metadata 
from the data store.
+     * @return the newly created metadata, or {@code null} if unknown.
+     * @throws DataStoreException if an error occurred while reading metadata 
from this resource.
      */
-    protected void createMetadata(final MetadataBuilder metadata) throws 
DataStoreException {
-        // Note: title is mandatory in ISO metadata, contrarily to the 
identifier.
-        getIdentifier().ifPresent((name) -> metadata.addTitle(new 
Sentence(name)));
-        getEnvelope().ifPresent((envelope) -> {
-            try {
-                metadata.addExtent(envelope);
-            } catch (TransformException | UnsupportedOperationException e) {
-                warning(e);
-            }
-        });
-    }
-
-    /**
-     * An international string where localized identifiers are formatted more 
like an English sentence.
-     * This is used for wrapping {@link GenericName#toInternationalString()} 
representation for use as
-     * a citation title.
-     */
-    private static final class Sentence extends AbstractInternationalString {
-        /** The generic name localized representation. */
-        private final InternationalString name;
-
-        /** Returns a new wrapper for the given generic name. */
-        Sentence(final GenericName name) {
-            this.name = name.toInternationalString();
-        }
-
-        /** Returns the generic name as an English-like sentence. */
-        @Override public String toString(final Locale locale) {
-            return 
CharSequences.camelCaseToSentence(name.toString(locale)).toString();
-        }
-
-        /** Returns a hash code value for this sentence. */
-        @Override public int hashCode() {
-            return ~name.hashCode();
-        }
-
-        /** Compares the given object with this sentence for equality. */
-        @Override public boolean equals(final Object other) {
-            return (other instanceof Sentence) && name.equals(((Sentence) 
other).name);
-        }
+    protected Metadata createMetadata() throws DataStoreException {
+        final MetadataBuilder builder = new MetadataBuilder();
+        builder.addDefaultMetadata(this, this);
+        return builder.build(true);
     }
 
     /**
      * Clears any cache in this resource, forcing the data to be recomputed 
when needed again.
-     * This method should be invoked if the data in underlying data store 
changed.
+     * The default implementation clears the cached metadata object, which 
will cause
+     * {@link #createMetadata()} to be invoked again when first needed.
      */
     protected void clearCache() {
-        synchronized (getSynchronizationLock()) {
-            metadata = null;
-        }
+        metadata = null;
     }
 
     /**
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AggregatedFeatureSet.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AggregatedFeatureSet.java
index 3f91724..9d9ad80 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AggregatedFeatureSet.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AggregatedFeatureSet.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Optional;
 import org.opengis.geometry.Envelope;
+import org.opengis.metadata.Metadata;
 import org.opengis.metadata.maintenance.ScopeCode;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.geometry.ImmutableEnvelope;
@@ -141,13 +142,15 @@ abstract class AggregatedFeatureSet extends 
AbstractFeatureSet {
      * @throws DataStoreException if an error occurred while reading metadata 
from the data stores.
      */
     @Override
-    protected void createMetadata(final MetadataBuilder metadata) throws 
DataStoreException {
-        super.createMetadata(metadata);
+    protected Metadata createMetadata() throws DataStoreException {
+        final MetadataBuilder metadata = new MetadataBuilder();
+        metadata.addDefaultMetadata(this, this);
         for (final FeatureSet fs : dependencies()) {
             final FeatureType type = fs.getType();
             metadata.addSource(fs.getMetadata(), ScopeCode.FEATURE_TYPE,
                     (type == null) ? null : new CharSequence[] 
{type.getName().toInternationalString()});
         }
+        return metadata.build(true);
     }
 
     /**
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ConcatenatedFeatureSet.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ConcatenatedFeatureSet.java
index ce758ad..8e0d0dc 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ConcatenatedFeatureSet.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ConcatenatedFeatureSet.java
@@ -51,7 +51,7 @@ import org.opengis.feature.FeatureType;
  *
  * @author  Alexis Manin (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.2
  * @since   1.0
  * @module
  */
@@ -175,7 +175,7 @@ public class ConcatenatedFeatureSet extends 
AggregatedFeatureSet {
      * @return estimation of the number of features.
      */
     @Override
-    protected OptionalLong getFeatureCount() {
+    public OptionalLong getFeatureCount() {
         long sum = 0;
         for (final FeatureSet fs : sources) {
             if (fs instanceof AbstractFeatureSet) {
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MemoryFeatureSet.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MemoryFeatureSet.java
index 061bbdf..c85de1d 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MemoryFeatureSet.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MemoryFeatureSet.java
@@ -33,7 +33,7 @@ import org.opengis.feature.FeatureType;
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.2
  * @since   1.0
  * @module
  */
@@ -82,7 +82,7 @@ public class MemoryFeatureSet extends AbstractFeatureSet {
      * @return the number of features.
      */
     @Override
-    protected OptionalLong getFeatureCount() {
+    public OptionalLong getFeatureCount() {
         return OptionalLong.of(features.size());
     }
 
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 ca6d322..99a9885 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,12 +73,15 @@ 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.DataStoreException;
+import org.apache.sis.storage.event.StoreListeners;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.internal.metadata.Merger;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.internal.util.Strings;
+import org.apache.sis.util.AbstractInternationalString;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.CharSequences;
@@ -812,6 +815,85 @@ public class MetadataBuilder {
     }
 
     /**
+     * Adds default metadata for the specified resource.
+     * This is used for default implementation of {@link 
AbstractResource#createMetadata()}.
+     *
+     * @param  resource   the resource for which to add metadata.
+     * @param  listeners  the listeners to notify in case of warning.
+     * @throws DataStoreException if an error occurred while reading metadata 
from the data store.
+     */
+    public final void addDefaultMetadata(final AbstractResource resource, 
final StoreListeners listeners) throws DataStoreException {
+        // Note: title is mandatory in ISO metadata, contrarily to the 
identifier.
+        resource.getIdentifier().ifPresent((name) -> addTitle(new 
Sentence(name)));
+        resource.getEnvelope().ifPresent((envelope) -> {
+            try {
+                addExtent(envelope);
+            } catch (TransformException | UnsupportedOperationException e) {
+                listeners.warning(e);
+            }
+        });
+    }
+
+    /**
+     * Adds default metadata for the specified resource.
+     * This is used for default implementation of {@link 
AbstractGridResource#createMetadata()}.
+     *
+     * @param  resource   the resource for which to add metadata.
+     * @param  listeners  the listeners to notify in case of warning.
+     * @throws DataStoreException if an error occurred while reading metadata 
from the data store.
+     */
+    public final void addDefaultMetadata(final AbstractFeatureSet resource, 
final StoreListeners listeners) throws DataStoreException {
+        addDefaultMetadata((AbstractResource) resource, listeners);
+        addFeatureType(resource.getType(), 
resource.getFeatureCount().orElse(-1));
+    }
+
+    /**
+     * Adds default metadata for the specified resource.
+     * This is used for default implementation of {@link 
AbstractGridResource#createMetadata()}.
+     *
+     * @param  resource   the resource for which to add metadata.
+     * @param  listeners  the listeners to notify in case of warning.
+     * @throws DataStoreException if an error occurred while reading metadata 
from the data store.
+     */
+    public final void addDefaultMetadata(final AbstractGridResource resource, 
final StoreListeners listeners) throws DataStoreException {
+        addDefaultMetadata((AbstractResource) resource, listeners);
+        addSpatialRepresentation(null, resource.getGridGeometry(), false);
+        for (final SampleDimension band : resource.getSampleDimensions()) {
+            addNewBand(band);
+        }
+    }
+
+    /**
+     * An international string where localized identifiers are formatted more 
like an English sentence.
+     * This is used for wrapping {@link GenericName#toInternationalString()} 
representation for use as
+     * a citation title.
+     */
+    private static final class Sentence extends AbstractInternationalString {
+        /** The generic name localized representation. */
+        private final InternationalString name;
+
+        /** Returns a new wrapper for the given generic name. */
+        Sentence(final GenericName name) {
+            this.name = name.toInternationalString();
+        }
+
+        /** Returns the generic name as an English-like sentence. */
+        @Override public String toString(final Locale locale) {
+            return 
CharSequences.camelCaseToSentence(name.toString(locale)).toString();
+        }
+
+        /** Returns a hash code value for this sentence. */
+        @Override public int hashCode() {
+            return ~name.hashCode();
+        }
+
+        /** Compares the given object with this sentence for equality. */
+        @Override public boolean equals(final Object other) {
+            return (other instanceof Sentence) && name.equals(((Sentence) 
other).name);
+        }
+    }
+
+    /**
      * Creates or fetches a citation for the given title. The same citation 
may be shared by many metadata objects,
      * for example identifiers or groups of keywords. Current implementation 
creates a {@link DefaultCitation} for
      * the given title and caches the result. Future implementations may 
return predefined citation constants from

Reply via email to