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