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 292697f58c2f01450b0fcd85c75d29492b7cb6bd Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Thu Mar 13 18:33:15 2025 +0100 Improve the SQL store metadata with information about the spatial schema, database version and (if applicable) PostGIS version. --- .../apache/sis/storage/landsat/MetadataReader.java | 16 ++-- .../sis/storage/netcdf/ucar/DecoderWrapper.java | 3 +- .../main/org/apache/sis/storage/sql/SQLStore.java | 25 +++-- .../apache/sis/storage/sql/feature/Database.java | 27 ++++++ .../sis/storage/sql/feature/SpatialSchema.java | 16 +++- .../apache/sis/storage/sql/postgis/Postgres.java | 15 +++ .../sis/storage/sql/postgis/PostgresTest.java | 25 ++++- .../apache/sis/storage/base/MetadataBuilder.java | 103 +++++++++++++++------ .../main/org/apache/sis/storage/csv/Store.java | 2 +- .../apache/sis/storage/image/WorldFileStore.java | 4 +- .../org/apache/sis/util/resources/Vocabulary.java | 5 + .../sis/util/resources/Vocabulary.properties | 1 + .../sis/util/resources/Vocabulary_fr.properties | 1 + .../org/apache/sis/storage/gdal/GDALStore.java | 4 +- 14 files changed, 188 insertions(+), 59 deletions(-) diff --git a/endorsed/src/org.apache.sis.storage.earthobservation/main/org/apache/sis/storage/landsat/MetadataReader.java b/endorsed/src/org.apache.sis.storage.earthobservation/main/org/apache/sis/storage/landsat/MetadataReader.java index a4cf6a0906..110830281d 100644 --- a/endorsed/src/org.apache.sis.storage.earthobservation/main/org/apache/sis/storage/landsat/MetadataReader.java +++ b/endorsed/src/org.apache.sis.storage.earthobservation/main/org/apache/sis/storage/landsat/MetadataReader.java @@ -340,7 +340,7 @@ final class MetadataReader extends MetadataBuilder { * @throws NumberFormatException if the given value cannot be parsed. */ private Double parseDouble(final String value) throws NumberFormatException { - return shared(Double.valueOf(value)); + return shared(Double.parseDouble(value)); } /** @@ -910,16 +910,16 @@ final class MetadataReader extends MetadataBuilder { * Set information about all non-null bands. The bands are categorized in three groups: * PANCHROMATIC, REFLECTIVE and THERMAL. */ - final DefaultCoverageDescription content = (DefaultCoverageDescription) singletonOrNull(result.getContentInfo()); + final var content = (DefaultCoverageDescription) singletonOrNull(result.getContentInfo()); if (content != null) { - final EnumMap<BandGroupName,DefaultAttributeGroup> groups = new EnumMap<>(BandGroupName.class); + final var groups = new EnumMap<BandGroupName,DefaultAttributeGroup>(BandGroupName.class); for (final EnumMap.Entry<BandName,Band> entry : bands.entrySet()) { - final DefaultAttributeGroup group = groups.computeIfAbsent(entry.getKey().group, (k) -> { - DefaultAttributeGroup g = new DefaultAttributeGroup(CoverageContentType.PHYSICAL_MEASUREMENT, null); - content.getAttributeGroups().add(g); - return g; + final DefaultAttributeGroup g = groups.computeIfAbsent(entry.getKey().group, (k) -> { + var ag = new DefaultAttributeGroup(CoverageContentType.PHYSICAL_MEASUREMENT, null); + content.getAttributeGroups().add(ag); + return ag; }); - group.getAttributes().add(entry.getValue().sampleDimension); + g.getAttributes().add(entry.getValue().sampleDimension); } } result.transitionTo(DefaultMetadata.State.FINAL); diff --git a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/ucar/DecoderWrapper.java b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/ucar/DecoderWrapper.java index 5f813e9e2f..479018bbd3 100644 --- a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/ucar/DecoderWrapper.java +++ b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/ucar/DecoderWrapper.java @@ -50,7 +50,6 @@ import org.apache.sis.util.collection.TreeTable; import org.apache.sis.util.collection.TableColumn; import org.apache.sis.metadata.sql.MetadataSource; import org.apache.sis.metadata.sql.MetadataStoreException; -import org.apache.sis.referencing.ImmutableIdentifier; import org.apache.sis.storage.DataStore; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.base.MetadataBuilder; @@ -216,7 +215,7 @@ public final class DecoderWrapper extends Decoder implements CancelTask { } catch (MetadataStoreException e) { provider = null; } - builder.addFormatReader(new ImmutableIdentifier(provider, "UCAR", Constants.NETCDF), getVersion()); + builder.addFormatReader((provider != null) ? provider.getTitle() : Constants.NETCDF, getVersion()); } /** diff --git a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/SQLStore.java b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/SQLStore.java index 6f53770f48..6e2f0361b9 100644 --- a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/SQLStore.java +++ b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/SQLStore.java @@ -30,7 +30,6 @@ import java.lang.reflect.Method; import org.opengis.util.GenericName; import org.opengis.metadata.Metadata; import org.opengis.parameter.ParameterValueGroup; -import org.opengis.metadata.spatial.SpatialRepresentationType; import org.apache.sis.storage.Resource; import org.apache.sis.storage.Aggregate; import org.apache.sis.storage.DataStore; @@ -83,11 +82,9 @@ public abstract class SQLStore extends DataStore implements Aggregate { * @see #getMetadata() */ private static final String[] TITLE_GETTERS = { - "getDescription", // PostgreSQL, SQL Server + "getApplicationName", // PostgreSQL "getDataSourceName", // Derby - "getDatabaseName", // Derby, PostgreSQL, SQL Server, SQLite - "getUrl", // PostgreSQL, SQLite - "getURL" // SQL Server + "getDatabaseName" // Derby, PostgreSQL, SQL Server, SQLite }; /** @@ -101,6 +98,18 @@ public abstract class SQLStore extends DataStore implements Aggregate { "getDataSourceName" }; + /** + * Names of possible public getter methods for fetching additional citation details. + * Elements are sorted in preference order. The return value shall be a {@link String}. + * On PostgreSQL, the return value is a hard-coded string such as + * "Non-Pooling DataSource from PostgreSQL JDBC Driver 42.7.3" + * + * @see #getIdentifier() + */ + private static final String[] DETAILS_GETTERS = { + "getDescription" // PostgreSQL, SQL Server + }; + /** * The data source to use for obtaining connections to the database. * This is the storage given (indirectly, through the {@link StorageConnector} argument) at construction time. @@ -417,14 +426,14 @@ public abstract class SQLStore extends DataStore implements Aggregate { public synchronized Metadata getMetadata() throws DataStoreException { if (metadata == null) { final var builder = new MetadataBuilder(); - builder.addSpatialRepresentation(SpatialRepresentationType.TEXT_TABLE); + builder.addIdentifier(identifier, MetadataBuilder.Scope.RESOURCE); + getDataSourceProperty(TITLE_GETTERS).ifPresent(builder::addTitle); + getDataSourceProperty(DETAILS_GETTERS).ifPresent(builder::addOtherCitationDetails); try (Connection c = source.getConnection()) { model(c).metadata(c.getMetaData(), builder); } catch (Exception e) { throw cannotExecute(e); } - getDataSourceProperty(TITLE_GETTERS).ifPresent(builder::addTitle); - builder.addIdentifier(identifier, MetadataBuilder.Scope.ALL); metadata = builder.buildAndFreeze(); } return metadata; diff --git a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Database.java b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Database.java index dd98a4f350..983db64e53 100644 --- a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Database.java +++ b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Database.java @@ -57,6 +57,8 @@ import org.apache.sis.util.Debug; import org.apache.sis.util.collection.TreeTable; import org.apache.sis.util.collection.Cache; import org.apache.sis.util.privy.UnmodifiableArrayList; +import org.apache.sis.util.resources.Vocabulary; +import org.opengis.metadata.citation.PresentationForm; /** @@ -465,6 +467,8 @@ public class Database<G> extends Syntax { * @throws SQLException if an error occurred while fetching table information. */ public final void metadata(final DatabaseMetaData metadata, final MetadataBuilder builder) throws SQLException { + builder.addPresentationForm(PresentationForm.TABLE_DIGITAL); + builder.addSpatialRepresentation(SpatialRepresentationType.TEXT_TABLE); if (hasGeometry) { builder.addSpatialRepresentation(SpatialRepresentationType.VECTOR); } @@ -474,6 +478,29 @@ public class Database<G> extends Syntax { for (final Table table : tables) { builder.addFeatureType(table.featureType, table.countRows(metadata, false, false)); } + builder.addFormatName((spatialSchema != null) ? spatialSchema.name : "SQL database"); + String server = metadata.getDatabaseProductName(); + if (server != null && !server.isBlank()) { + CharSequence description = server; + final String version = metadata.getDatabaseProductVersion(); + if (version != null) { + description = Vocabulary.formatInternational(Vocabulary.Keys.Version_2, server, version); + } + builder.addFormatCitationDetails(completeDatabaseVersion(description)); + } + builder.addFormatReaderSIS("SQL"); // Value of SQLStoreProvider.NAME. + } + + /** + * Completes the given database version with information such as the version of the geospatial extension. + * For example, in the case of the PostgreSL database, this will add the PostGIS version. + * The default implementation returns the given text unchanged. + * + * @param version name and version of the database. + * @return given text, optionally completed with additional information. + */ + protected CharSequence completeDatabaseVersion(CharSequence version) { + return version; } /** diff --git a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SpatialSchema.java b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SpatialSchema.java index 712eeb0e36..d4f2b34b9a 100644 --- a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SpatialSchema.java +++ b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SpatialSchema.java @@ -36,11 +36,12 @@ import java.util.Map; */ public enum SpatialSchema { /** - * Table and column names as specified by Geopackage. This is the same thing as {@link #SQL_MM} + * Table and column names as specified by GeoPackage. This is the same thing as {@link #SQL_MM} * except for table names, for the case (Geopackage uses lower case) and for the addition of a * {@code geometry_type_name} column. */ GEOPACKAGE( + "GeoPackage", // Human-readable name of this spaial schema. "gpkg_spatial_ref_sys", // Table for Spatial Reference System definitions. "srs_name", // Column for CRS names. "srs_id", // Column for CRS identifiers. @@ -74,7 +75,8 @@ public enum SpatialSchema { * In Geopackage, this table is named {@code "gpkg_spatial_ref_sys"} but otherwise has identical content * except for the case (Geopackage uses lower case). */ - SQL_MM("ST_SPATIAL_REFERENCE_SYSTEMS", // Table for Spatial Reference System definitions. + SQL_MM("ISO-13249 SQL/MM", // Human-readable name of this spaial schema. + "ST_SPATIAL_REFERENCE_SYSTEMS", // Table for Spatial Reference System definitions. "SRS_NAME", // Column for CRS names. "SRS_ID", // Column for CRS identifiers. "ORGANIZATION", // Column for CRS authority names. @@ -104,6 +106,7 @@ public enum SpatialSchema { * } */ SIMPLE_FEATURE( + "ISO 19125 / OGC Simple feature", // Human-readable name of this spaial schema. "SPATIAL_REF_SYS", // Table for Spatial Reference System definitions. null, // Column for CRS names, or `null` if none. "SRID", // Column for CRS identifiers. @@ -119,6 +122,11 @@ public enum SpatialSchema { "GEOMETRY_TYPE", // Column where the type of each geometry column is stored. GeometryTypeEncoding.NUMERIC); // How geometry types are encoded in the above-cited type column. + /** + * The name of this spatial schema. + */ + final String name; + /** * Name of the table for Spatial Reference System definitions. * Example: {@code "SPATIAL_REF_SYS"}, {@code "ST_SPATIAL_REFERENCE_SYSTEMS"}. @@ -205,6 +213,7 @@ public enum SpatialSchema { /** * Creates a new enumeration value. * + * @param name name of this spatial schema. * @param crsTable name of the table for Spatial Reference System definitions. * @param crsNameColumn name of the column for CRS names, or {@code null} if none. * @param crsIdentifierColumn name of the column for CRS identifiers. @@ -220,13 +229,14 @@ public enum SpatialSchema { * @param geomTypeColumn name of the column where the type of each geometry column is stored, or null if none. * @param typeEncoding how geometry types are encoded in the {@link #geomTypeColumn}. */ - private SpatialSchema(String crsTable, String crsNameColumn, String crsIdentifierColumn, + private SpatialSchema(String name, String crsTable, String crsNameColumn, String crsIdentifierColumn, String crsAuthorityNameColumn, String crsAuthorityCodeColumn, Map<CRSEncoding,String> crsDefinitionColumn, String crsDescriptionColumn, String geometryColumns, String geomCatalogColumn, String geomSchemaColumn, String geomTableColumn, String geomColNameColumn, String geomTypeColumn, GeometryTypeEncoding typeEncoding) { + this.name = name; this.crsTable = crsTable; this.crsNameColumn = crsNameColumn; this.crsIdentifierColumn = crsIdentifierColumn; diff --git a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/postgis/Postgres.java b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/postgis/Postgres.java index 356cf07238..b5a6ab50ea 100644 --- a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/postgis/Postgres.java +++ b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/postgis/Postgres.java @@ -42,6 +42,7 @@ import org.apache.sis.storage.sql.feature.SpatialSchema; import org.apache.sis.metadata.sql.privy.Dialect; import org.apache.sis.storage.event.StoreListeners; import org.apache.sis.util.Version; +import org.apache.sis.util.resources.Vocabulary; /** @@ -120,6 +121,20 @@ public final class Postgres<G> extends Database<G> { return null; } + /** + * Completes the given database version with information about PostGIS version. + * + * @param version name and version of the database. + * @return given text, completed with PostGIS information if present. + */ + @Override + protected CharSequence completeDatabaseVersion(CharSequence version) { + if (version != null) { + return Vocabulary.formatInternational(Vocabulary.Keys.With_2, version, "PostGIS " + postgisVersion); + } + return version; + } + /** * Returns a function for getting values from a column having the given definition. * The given definition should include data SQL type and type name. diff --git a/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/postgis/PostgresTest.java b/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/postgis/PostgresTest.java index 05418638e6..2babdb9146 100644 --- a/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/postgis/PostgresTest.java +++ b/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/postgis/PostgresTest.java @@ -17,6 +17,7 @@ package org.apache.sis.storage.sql.postgis; import java.util.List; +import java.util.Arrays; import java.util.stream.Stream; import java.util.function.Supplier; import java.sql.Connection; @@ -30,6 +31,9 @@ import java.lang.reflect.Method; import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Geometry; import org.opengis.geometry.Envelope; +import org.opengis.metadata.Metadata; +import org.opengis.metadata.identification.Identification; +import org.opengis.metadata.spatial.SpatialRepresentationType; import org.opengis.util.FactoryException; import org.opengis.referencing.crs.ProjectedCRS; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -55,6 +59,7 @@ import org.junit.jupiter.api.parallel.ResourceLock; import org.apache.sis.storage.sql.SQLStoreTest; import org.apache.sis.storage.sql.feature.GeometryGetterTest; import org.apache.sis.test.TestCase; +import org.apache.sis.test.TestUtilities; import org.apache.sis.metadata.sql.TestDatabase; import org.apache.sis.referencing.crs.HardCodedCRS; @@ -99,6 +104,19 @@ public final class PostgresTest extends TestCase { assertNull ( version.getRevision()); } + /** + * Performs some verification of store metadata. + * + * @param metadata the metadata to verify. + */ + private static void validate(final Metadata metadata) { + final Identification identification = TestUtilities.getSingleton(metadata.getIdentificationInfo()); + assertTrue(identification.getSpatialRepresentationTypes().containsAll( + Arrays.asList(SpatialRepresentationType.TEXT_TABLE, + SpatialRepresentationType.VECTOR, + SpatialRepresentationType.GRID))); + } + /** * Tests reading and writing features and rasters. * @@ -109,17 +127,18 @@ public final class PostgresTest extends TestCase { public void testSpatialFeatures() throws Exception { try (TestDatabase database = TestDatabase.createOnPostgreSQL(SQLStoreTest.SCHEMA, true)) { database.executeSQL(List.of(resource("SpatialFeatures.sql"))); - final StorageConnector connector = new StorageConnector(database.source); + final var connector = new StorageConnector(database.source); connector.setOption(OptionKey.GEOMETRY_LIBRARY, GeometryLibrary.JTS); final ResourceDefinition table = ResourceDefinition.table(null, SQLStoreTest.SCHEMA, "SpatialData"); try (SimpleFeatureStore store = new SimpleFeatureStore(new SQLStoreProvider(), connector, table)) { + validate(store.getMetadata()); /* * Invoke the private `model()` method. We have to use reflection because the class * is not in the same package and we do not want to expose the method in public API. */ final Method modelAccessor = SQLStore.class.getDeclaredMethod("model"); modelAccessor.setAccessible(true); - final Postgres<?> pg = (Postgres<?>) modelAccessor.invoke(store); + final var pg = (Postgres<?>) modelAccessor.invoke(store); try (Connection connection = database.source.getConnection(); ExtendedInfo info = new ExtendedInfo(pg, connection)) { @@ -181,7 +200,7 @@ public final class PostgresTest extends TestCase { final ResultSet r = stmt.executeQuery(); assertTrue(r.next()); final ReadableByteChannel channel = Channels.newChannel(encoding.decode(r.getBinaryStream(1))); - final ChannelDataInput input = new ChannelDataInput(test.filename, channel, ByteBuffer.allocate(50), false); + final var input = new ChannelDataInput(test.filename, channel, ByteBuffer.allocate(50), false); RasterReaderTest.compareReadResult(test, reader, input); assertFalse(r.next()); } diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataBuilder.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataBuilder.java index f4ed3b4507..41a6938b8d 100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataBuilder.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataBuilder.java @@ -91,7 +91,6 @@ import org.apache.sis.metadata.sql.MetadataSource; import org.apache.sis.metadata.privy.Merger; import org.apache.sis.temporal.TimeMethods; import org.apache.sis.referencing.NamedIdentifier; -import org.apache.sis.referencing.ImmutableIdentifier; import org.apache.sis.referencing.privy.AxisDirections; import org.apache.sis.geometry.AbstractEnvelope; import org.apache.sis.storage.AbstractResource; @@ -1359,6 +1358,24 @@ public class MetadataBuilder { } } + /** + * Adds a mode in which the resource is represented. + * Storage location is: + * + * <ul> + * <li>{@code metadata/identificationInfo/citation/presentationForm}</li> + * </ul> + * + * @param form mode in which the resource is represented, or {@code null} if none. + * + * @see #addSpatialRepresentation(SpatialRepresentationType) + */ + public final void addPresentationForm(final PresentationForm form) { + if (form != null) { + citation().getPresentationForms().add(form); + } + } + /** * Adds a brief narrative summary of the resource(s). * If a summary already exists, the new one will be appended after a new line. @@ -1413,6 +1430,8 @@ public class MetadataBuilder { * </ul> * * @param details other details, or {@code null} for no-operation. + * + * @see #addFormatCitationDetails(CharSequence) */ public final void addOtherCitationDetails(final CharSequence details) { final InternationalString i18n = trim(details); @@ -1898,6 +1917,8 @@ public class MetadataBuilder { * </ul> * * @param type method used to spatially represent geographic information, or {@code null} for no-operation. + * + * @see #addPresentationForm(PresentationForm) */ public final void addSpatialRepresentation(final SpatialRepresentationType type) { if (type != null) { @@ -3041,7 +3062,7 @@ public class MetadataBuilder { * used as an alternative name of the current formaT. Storage location is: * * <ul> - * <li>{@code metadata/identificationInfo/resourceFormat/formatSpecificationCitation/alternateTitle}</li> + * <li>{@code metadata/identificationInfo/resourceFormat/formatSpecificationCitation/[alternate]Title}</li> * </ul> * * If this method is used together with {@link #setPredefinedFormat setPredefinedFormat(…)}, @@ -3053,6 +3074,16 @@ public class MetadataBuilder { * @see #addCompression(CharSequence) */ public final void addFormatName(final CharSequence value) { + addFormatName(value, true); + } + + /** + * Adds a name to the resource format, optionally doing nothing if a name is already present. + * + * @param value the format name, or {@code null} for no-operation. + * @param more {@code false} for doing nothing if a name is already present. + */ + private void addFormatName(final CharSequence value, final boolean more) { final InternationalString i18n = trim(value); if (i18n != null) { @SuppressWarnings("LocalVariableHidesMemberVariable") @@ -3060,7 +3091,7 @@ public class MetadataBuilder { DefaultCitation c = DefaultCitation.castOrCopy(format.getFormatSpecificationCitation()); if (c == null) { c = new DefaultCitation(i18n); - } else { + } else if (more) { addIfNotPresent(c.getAlternateTitles(), i18n); } format.setFormatSpecificationCitation(c); @@ -3103,49 +3134,63 @@ public class MetadataBuilder { } /** - * Adds a note about which reader is used. This method should not be invoked before - * the {@linkplain #addFormatName format name} has been set. Storage location is: + * Adds other information about the format. Apache SIS currently uses this + * location for specifying which software was used for reading the data. + * Storage location is: * * <ul> - * <li>{@code metadata/identificationInfo/resourceFormat/formatSpecificationCitation/identifier}</li> * <li>{@code metadata/identificationInfo/resourceFormat/formatSpecificationCitation/otherCitationDetails}</li> * </ul> * + * @param details other details about the format, or {@code null} for no-operation. + * + * @see #addOtherCitationDetails(CharSequence) + */ + public final void addFormatCitationDetails(final CharSequence details) { + final InternationalString i18n = trim(details); + if (i18n != null) { + addIfNotPresent(getFormatCitation().getOtherCitationDetails(), i18n); + } + } + + /** + * Adds a note about which reader is used. Apache SIS currently stores this information as other format + * citation details, but this location may change in a SIS future version if we find a better location. + * Storage location is as below, wrapped in a "Read by {0} version {1}" text: + * + * <ul> + * <li>{@code metadata/identificationInfo/resourceFormat/formatSpecificationCitation/otherCitationDetails}</li> + * </ul> + * + * This method should not be invoked before the {@linkplain #addFormatName format name} has been set. * If this method is used together with {@link #setPredefinedFormat setPredefinedFormat(…)}, * then the predefined format should be set <strong>before</strong> this method. * - * @param driver library-specific way to identify the format (mandatory). + * <h4>Usage</h4> + * This method is invoked for various libraries such as GDAL, UCAR NetCDF and Apache SIS. + * For the specific case of Apache SIS, {@link #addFormatReaderSIS(String)} is a shortcut. + * + * @param driver driver name to be wrapped in a "Read by {0} version {1}" text, or {@code null} for no-operation. * @param version the library version, or {@code null} if unknown. */ - public final void addFormatReader(final Identifier driver, final Version version) { - CharSequence title = null; - Citation authority = driver.getAuthority(); - if (authority != null) { - title = authority.getTitle(); - if (title != null) { - for (CharSequence t : authority.getAlternateTitles()) { - if (t.length() < title.length()) { - title = t; // Alternate titles are often abbreviations. - } - } - } + public final void addFormatReader(final CharSequence driver, final Version version) { + if (driver != null) { + addFormatCitationDetails(Resources.formatInternational(Resources.Keys.ReadBy_2, driver, + (version != null) ? version : Vocabulary.formatInternational(Vocabulary.Keys.Unspecified))); } - final DefaultCitation c = getFormatCitation(); - addIfNotPresent(c.getOtherCitationDetails(), - Resources.formatInternational(Resources.Keys.ReadBy_2, (title != null) ? title : driver.getCodeSpace(), - (version != null) ? version : Vocabulary.formatInternational(Vocabulary.Keys.Unspecified))); } /** * Adds a note saying that Apache <abbr>SIS</abbr> has been used for decoding the format. - * This method should not be invoked before the {@linkplain #addFormatName format name} has been set. + * The argument should be the short name (identifier) of the {@code DataStoreProvider}. + * It is currently used only as a fallback when the {@linkplain #addFormatName format name} + * has not been set. * - * @param name the format name, or {@code null} if unspecified. + * @param provider the provider name, or {@code null} if unknown. */ - public void addFormatReaderSIS(final String name) { - if (name != null) { - addFormatReader(new ImmutableIdentifier(Citations.SIS, Constants.SIS, name), Version.SIS); - } + public void addFormatReaderSIS(final String provider) { + addFormatName(provider, false); + addFormatReader(Citations.SIS.getTitle(), Version.SIS); } /** diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/csv/Store.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/csv/Store.java index 9f9668999a..b7a34724eb 100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/csv/Store.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/csv/Store.java @@ -624,7 +624,7 @@ final class Store extends URIDataStore implements FeatureSet { @Override public synchronized Metadata getMetadata() throws DataStoreException { if (metadata == null) { - final MetadataBuilder builder = new MetadataBuilder(); + final var builder = new MetadataBuilder(); final String format = (timeEncoding != null) && hasTrajectories ? StoreProvider.MOVING : StoreProvider.NAME; builder.setPredefinedFormat(format, listeners, true); builder.addFormatReaderSIS(format); diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java index 957e45bb34..e58db7265f 100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java @@ -523,7 +523,7 @@ loop: for (int convention=0;; convention++) { @Override public synchronized Metadata getMetadata() throws DataStoreException { if (metadata == null) try { - final MetadataBuilder builder = new MetadataBuilder(); + final var builder = new MetadataBuilder(); String format = reader().getFormatName(); for (final String key : KNOWN_FORMATS) { if (key.equalsIgnoreCase(format)) { @@ -533,7 +533,7 @@ loop: for (int convention=0;; convention++) { break; } } - builder.addFormatName(format); // Does nothing if `format` is null. + builder.addFormatName(format); // Does nothing if `format` is null. builder.addFormatReaderSIS(WorldFileStoreProvider.NAME); builder.addResourceScope(ScopeCode.COVERAGE, null); builder.addSpatialRepresentation(null, getGridGeometry(MAIN_IMAGE), true); diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.java index 4887863f70..9964ee5081 100644 --- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.java +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.java @@ -1414,6 +1414,11 @@ public class Vocabulary extends IndexedResourceBundle { */ public static final short Width = 224; + /** + * {0} with {1}. + */ + public static final short With_2 = 277; + /** * World */ diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.properties b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.properties index 4bfda2c7a8..5c17e977e3 100644 --- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.properties +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.properties @@ -287,6 +287,7 @@ Visual = Visual Warnings = Warnings WestBound = West bound Width = Width +With_2 = {0} with {1}. World = World Write = Write Yellow = Yellow diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary_fr.properties b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary_fr.properties index 18ca8ac441..e7d786b763 100644 --- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary_fr.properties +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary_fr.properties @@ -294,6 +294,7 @@ Visual = Visuel Warnings = Avertissements WestBound = Limite ouest Width = Largeur +With_2 = {0} avec {1}. World = Monde Write = \u00c9criture Yellow = Jaune diff --git a/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java b/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java index 98f58e15c4..d1622c988d 100644 --- a/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java +++ b/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java @@ -38,7 +38,6 @@ import org.apache.sis.io.wkt.WKTFormat; import org.apache.sis.io.wkt.Convention; import org.apache.sis.io.stream.IOUtilities; import org.apache.sis.metadata.iso.citation.Citations; -import org.apache.sis.referencing.ImmutableIdentifier; import org.apache.sis.setup.GeometryLibrary; import org.apache.sis.setup.OptionKey; import org.apache.sis.storage.AbstractResource; @@ -361,8 +360,7 @@ public class GDALStore extends DataStore implements Aggregate { builder.addFormatName(driver.getName()); String id = driver.getIdentifier(); if (id != null) { - builder.addFormatReader(new ImmutableIdentifier(Citations.GDAL, Constants.GDAL, id), - getProvider().getVersion().orElse(null)); + builder.addFormatReader(Citations.GDAL.getTitle(), getProvider().getVersion().orElse(null)); } } }