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 49f489db9e123d9a649331513b209c69140ddb63 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Thu Dec 26 15:20:34 2024 +0100 Fix an `IndexOutOfBoundsException` when fetching netCDF metadata. Adjust the title of GeoTIFF file declared in metadata. --- .../apache/sis/storage/geotiff/GeoTiffStore.java | 9 ++++- .../sis/storage/geotiff/ImageFileDirectory.java | 27 ++++---------- .../apache/sis/storage/netcdf/MetadataReader.java | 1 + .../apache/sis/storage/base/MetadataBuilder.java | 43 +++++++++++++++------- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/GeoTiffStore.java b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/GeoTiffStore.java index 34925a609e..d17f719e18 100644 --- a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/GeoTiffStore.java +++ b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/GeoTiffStore.java @@ -67,6 +67,7 @@ import org.apache.sis.coverage.SubspaceNotSpecifiedException; import org.apache.sis.coverage.grid.GridCoverage; import org.apache.sis.coverage.grid.GridGeometry; import org.apache.sis.util.ArgumentChecks; +import org.apache.sis.util.SimpleInternationalString; import org.apache.sis.util.privy.Constants; import org.apache.sis.util.privy.ListOfUnknownSize; import org.apache.sis.util.collection.BackingStoreException; @@ -420,7 +421,13 @@ public class GeoTiffStore extends DataStore implements Aggregate { * file did not specified any ImageDescription tag, then we will add the filename as a title instead of an * identifier because the title is mandatory in ISO 19115 metadata. */ - getIdentifier().ifPresent((id) -> builder.addTitleOrIdentifier(id.toString(), MetadataBuilder.Scope.ALL)); + getIdentifier().ifPresent((id) -> { + builder.addIdentifier(id, MetadataBuilder.Scope.ALL); + // Replace the `ResourceInternationalString` for "Image 1". + if (!(builder.getTitle() instanceof SimpleInternationalString)) { + builder.setTitle(id.toString()); + } + }); builder.setISOStandards(true); final DefaultMetadata md = builder.build(); metadata = customizer.customize(new SchemaModifier.Source(this), md); diff --git a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java index 23c8926929..7c4fc0e5cf 100644 --- a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java +++ b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java @@ -469,13 +469,6 @@ final class ImageFileDirectory extends DataCube { return reader.store.encoding; } - /** - * Returns the image index used in the default identifier. - */ - private String getImageIndex() { - return String.valueOf(index + 1); - } - /** * Returns the identifier in the namespace of the {@link GeoTiffStore}. * The first image has the sequence number "1", optionally customized. @@ -495,7 +488,8 @@ final class ImageFileDirectory extends DataCube { // Should not happen because `setOverviewIdentifier(…)` should have been invoked. return Optional.empty(); } - GenericName name = reader.nameFactory.createLocalName(reader.store.namespace(), getImageIndex()); + final String tip = String.valueOf(index + 1); + GenericName name = reader.nameFactory.createLocalName(reader.store.namespace(), tip); name = name.toFullyQualifiedName(); // Because "1" alone is not very informative. final var source = new SchemaModifier.Source(reader.store, index, getDataType()); identifier = reader.store.customizer.customize(source, name); @@ -1389,19 +1383,12 @@ final class ImageFileDirectory extends DataCube { source = null; // Note: the `index` value is invalid in this case. } else { source = new SchemaModifier.Source(reader.store, index, getDataType()); - } - getIdentifier().ifPresent((id) -> { - metadata.addIdentifier(id, ImageMetadataBuilder.Scope.RESOURCE); - final CharSequence title; - if (!getImageIndex().equals(id.tip().toString())) { - title = id.toString(); - } else if (source != null && !metadata.hasTitle()) { - title = Vocabulary.formatInternational(Vocabulary.Keys.Image_1, index + 1); - } else { - return; // Return from lambda, not from `createMetadata()`. + if (metadata.getTitle() == null) { + // Note: `GeoTiffStore.getMetadata()` relies on this value not being a `String`. + metadata.addTitle(Vocabulary.formatInternational(Vocabulary.Keys.Image_1, index + 1)); } - metadata.addTitle(title); - }); + } + metadata.addIdentifier(getIdentifier().orElse(null), ImageMetadataBuilder.Scope.RESOURCE); /* * Add information about sample dimensions. * diff --git a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/MetadataReader.java b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/MetadataReader.java index ec5dcd4b19..a8cd6c636b 100644 --- a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/MetadataReader.java +++ b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/MetadataReader.java @@ -766,6 +766,7 @@ split: while ((start = CharSequences.skipLeadingWhitespaces(value, start, lengt /* * If at least one vertical coordinate is available, add a VerticalExtent. */ + convention = Math.min(convention, VERTICAL_ALTERNATIVES.length - 1); for (int i=0; i <= convention; i++) { if (fillExtent(VERTICAL_ALTERNATIVES[i], Units.METRE, null, extent, 0)) { addVerticalExtent(extent[0], extent[1], verticalCRS); 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 258a05277a..9d5f24b289 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 @@ -851,7 +851,7 @@ public class MetadataBuilder { * @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 { - if (!hasTitle()) { + if (getTitle() == null) { // Note: title is mandatory in ISO metadata, contrarily to the identifier. resource.getIdentifier().ifPresent((name) -> addTitle(new Sentence(name))); } @@ -1200,6 +1200,33 @@ public class MetadataBuilder { return s1.toString().equals(s2.toString()); } + /** + * Returns the current citation title. This method is typically used for deciding + * whether to use some fallback as title, because titles are mandatory in ISO 19115. + * + * @return the title defined in the current citation, or {@code null} if none. + */ + public final InternationalString getTitle() { + return (citation != null) ? citation.getTitle() : null; + } + + /** + * Sets the title, replacing any previous value. + * Storage location is: + * + * <ul> + * <li>{@code metadata/identificationInfo/citation/title}</li> + * </ul> + * + * @param title the resource title, or {@code null} for no-operation. + */ + public final void setTitle(final CharSequence title) { + final InternationalString i18n = trim(title); + if (i18n != null) { + citation().setTitle(i18n); + } + } + /** * Adds a title or alternate title of the resource, if not already present. * This operation does nothing if the title is already defined and the given @@ -1248,7 +1275,7 @@ public class MetadataBuilder { */ public final void addTitleOrIdentifier(final String code, Scope scope) { if (scope != Scope.METADATA) { - if (!hasTitle()) { + if (getTitle() == null) { addTitle(code); if (scope == Scope.RESOURCE) { return; @@ -1259,16 +1286,6 @@ public class MetadataBuilder { addIdentifier(null, code, scope); } - /** - * Returns whether the current citation has a title. This method is typically used for deciding - * whether to use some fallback as title, because titles are mandatory in ISO 19115. - * - * @return whether a title is defined in the current citation. - */ - public final boolean hasTitle() { - return (citation != null) && citation.getTitle() != null; - } - /** * Adds a version of the resource. * If a version already exists, the new one will be appended after a new line. @@ -3233,7 +3250,7 @@ public class MetadataBuilder { * * We could easily factor out the above pattern in a method, but we don't do that because * it would invoke `bla().getFoos()` before the loop. We want that call to happen only if - * the collection contains at least one element. Usually there is only 0 or 1 element. + * the collection contains at least one element. Usually, there is only 0 or 1 element. */ for (final Identification info : component.getIdentificationInfo()) { final Citation c = info.getCitation();