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 ce6fa1494e6d21473005b7f827c29e835f6b2ab6 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Fri Jul 29 14:57:04 2022 +0200 Minor consolidation of the GeoTIFF name parsing. The only functional change is that the full namespace is checked instead of only the direct parent. This is for consistency with the default implementation of `findResource(String)` in `DataStore`. This comparison is easier to do on `String` objects, which also avoid creation of temporary `GenericName`. As minor changes, the separator is fetched from the namespace (but it should not make a difference in practice), the scope of `try ... catch` blocks is make narrower, the error message for unexpected namespace is localized, and the test case verifies the error message. --- .../java/org/apache/sis/util/iso/AbstractName.java | 15 ++--- .../apache/sis/util/iso/DefaultNameFactory.java | 9 +-- .../org/apache/sis/util/iso/DefaultNameSpace.java | 33 ++++++++++- .../org/apache/sis/util/iso/DefaultRecordType.java | 4 +- .../java/org/apache/sis/util/iso/package-info.java | 2 +- .../java/org/apache/sis/util/resources/Errors.java | 10 ++-- .../apache/sis/util/resources/Errors.properties | 2 +- .../apache/sis/util/resources/Errors_fr.properties | 2 +- .../apache/sis/storage/geotiff/GeoTiffStore.java | 67 +++++++++++----------- .../sis/storage/geotiff/SelfConsistencyTest.java | 29 ++++++---- 10 files changed, 99 insertions(+), 74 deletions(-) diff --git a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/AbstractName.java b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/AbstractName.java index 89e8faa7bb..26b31ae973 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/AbstractName.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/AbstractName.java @@ -277,14 +277,9 @@ public abstract class AbstractName implements GenericName, Serializable { * @param name the name after which to write a separator. * @return the separator to write after the given name. */ - static String separator(final GenericName name) { - if (name != null) { - final NameSpace scope = name.scope(); - if (scope instanceof DefaultNameSpace) { - return ((DefaultNameSpace) scope).headSeparator; - } - } - return DefaultNameSpace.DEFAULT_SEPARATOR_STRING; + private static String headSeparator(final GenericName name) { + return (name != null) ? DefaultNameSpace.getSeparator(name.scope(), true) + : DefaultNameSpace.DEFAULT_SEPARATOR_STRING; } /** @@ -310,7 +305,7 @@ public abstract class AbstractName implements GenericName, Serializable { final StringBuilder buffer = new StringBuilder(); for (final LocalName name : getParsedNames()) { if (insertSeparator) { - buffer.append(separator(name)); + buffer.append(headSeparator(name)); } insertSeparator = true; buffer.append(name); @@ -383,7 +378,7 @@ public abstract class AbstractName implements GenericName, Serializable { final StringBuilder buffer = new StringBuilder(); for (final LocalName name : parsedNames) { if (insertSeparator) { - buffer.append(separator(name)); + buffer.append(headSeparator(name)); } insertSeparator = true; buffer.append(name.toInternationalString().toString(locale)); diff --git a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java index cc78ce2a56..92fcc67b5d 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java @@ -37,8 +37,6 @@ import org.apache.sis.util.resources.Errors; import org.apache.sis.util.collection.WeakHashSet; import org.apache.sis.internal.util.Strings; -import static org.apache.sis.util.iso.DefaultNameSpace.DEFAULT_SEPARATOR_STRING; - /** * A factory for creating {@link AbstractName} objects. @@ -294,12 +292,7 @@ public class DefaultNameFactory extends AbstractFactory implements NameFactory { */ @Override public GenericName parseGenericName(final NameSpace scope, final CharSequence name) { - final String separator; - if (scope instanceof DefaultNameSpace) { - separator = ((DefaultNameSpace) scope).separator; - } else { - separator = DEFAULT_SEPARATOR_STRING; - } + final String separator = DefaultNameSpace.getSeparator(scope, false); final int s = separator.length(); final List<String> names = new ArrayList<>(); int lower = 0; diff --git a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java index d0ba3d84e7..9fbe7bdfb4 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java @@ -52,7 +52,7 @@ import static org.apache.sis.util.ArgumentChecks.ensureNonNull; * remain safe to call from multiple threads and do not change any public {@code NameSpace} state. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 0.8 + * @version 1.3 * * @see DefaultScopedName * @see DefaultLocalName @@ -98,12 +98,16 @@ public class DefaultNameSpace implements NameSpace, Serializable { /** * The separator to insert between the namespace and the {@linkplain AbstractName#head() head} * of any name in that namespace. + * + * @see #getSeparator(NameSpace, boolean) */ - final String headSeparator; + private final String headSeparator; /** * The separator to insert between the {@linkplain AbstractName#getParsedNames() parsed names} * of any name in that namespace. + * + * @see #getSeparator(NameSpace, boolean) */ final String separator; @@ -252,6 +256,31 @@ public class DefaultNameSpace implements NameSpace, Serializable { return ns; } + /** + * Returns the separator between name components in the given namespace. + * If the given namespace is an instance of {@code DefaultNameSpace}, then this method + * returns the {@code headSeparator} or {@code separator} argument given to the constructor. + * Otherwise this method returns the {@linkplain #DEFAULT_SEPARATOR default separator}. + * + * <div class="note"><b>API note:</b> + * this method is static because the {@code getSeparator(…)} method is not part of GeoAPI interfaces. + * A static method makes easier to use without {@code (if (x instanceof DefaultNameSpace)} checks.</div> + * + * @param ns the namespace for which to get the separator. May be {@code null}. + * @param head {@code true} for the separator between namespace and {@linkplain AbstractName#head() head}, or + * {@code false} for the separator between {@linkplain AbstractName#getParsedNames() parsed names}. + * @return separator between name components. + * + * @since 1.3 + */ + public static String getSeparator(final NameSpace ns, final boolean head) { + if (ns instanceof DefaultNameSpace) { + final DefaultNameSpace ds = (DefaultNameSpace) ns; + return head ? ds.headSeparator : ds.separator; + } + return DEFAULT_SEPARATOR_STRING; + } + /** * Indicates whether this namespace is a "top level" namespace. Global, or top-level * namespaces are not contained within another namespace. The global namespace has no diff --git a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultRecordType.java b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultRecordType.java index 19919c24b9..60d6bfc1ee 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultRecordType.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultRecordType.java @@ -169,7 +169,7 @@ public class DefaultRecordType extends RecordDefinition implements RecordType, S final LocalName schemaName = container.getSchemaName(); final GenericName fullTypeName = typeName.toFullyQualifiedName(); if (schemaName.compareTo(typeName.scope().name().tip()) != 0) { - throw new IllegalArgumentException(Errors.format(Errors.Keys.InconsistentNamespace_2, schemaName, fullTypeName)); + throw new IllegalArgumentException(Errors.format(Errors.Keys.UnexpectedNamespace_2, schemaName, fullTypeName)); } final int size = size(); for (int i=0; i<size; i++) { @@ -179,7 +179,7 @@ public class DefaultRecordType extends RecordDefinition implements RecordType, S throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalMemberType_2, name, type)); } if (fullTypeName.compareTo(name.scope().name()) != 0) { - throw new IllegalArgumentException(Errors.format(Errors.Keys.InconsistentNamespace_2, + throw new IllegalArgumentException(Errors.format(Errors.Keys.UnexpectedNamespace_2, fullTypeName, name.toFullyQualifiedName())); } } diff --git a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/package-info.java b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/package-info.java index fadfbb6c39..105afcdbb4 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/package-info.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/package-info.java @@ -99,7 +99,7 @@ * </table> * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.1 + * @version 1.3 * @since 0.3 * @module */ diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java index 7b3d1dfe90..de7a0030d2 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java +++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java @@ -476,11 +476,6 @@ public final class Errors extends IndexedResourceBundle { */ public static final short InconsistentAttribute_2 = 67; - /** - * Expected “{0}” namespace for “{1}”. - */ - public static final short InconsistentNamespace_2 = 68; - /** * Inconsistent table columns. */ @@ -896,6 +891,11 @@ public final class Errors extends IndexedResourceBundle { */ public static final short UnexpectedFileFormat_2 = 139; + /** + * The “{1}” name is not valid in this context, because the “{0}” namespace was expected. + */ + public static final short UnexpectedNamespace_2 = 68; + /** * Parameter “{0}” was not expected. */ diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties index 8ea926426d..ec0853a874 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties +++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties @@ -106,7 +106,6 @@ IncompatibleUnit_1 = Unit \u201c{0}\u201d is incompatible with cu IncompatibleUnits_2 = Units \u201c{0}\u201d and \u201c{1}\u201d are incompatible. IncompatibleUnitDimension_5 = The \u201c{0}\u201d unit of measurement has dimension of \u2018{1}\u2019 ({2}). It is incompatible with dimension of \u2018{3}\u2019 ({4}). InconsistentAttribute_2 = Value \u201c{1}\u201d of attribute \u2018{0}\u2019 is inconsistent with other attributes. -InconsistentNamespace_2 = Expected \u201c{0}\u201d namespace for \u201c{1}\u201d. InconsistentTableColumns = Inconsistent table columns. InconsistentUnitsForCS_1 = Unit of measurement \u201c{0}\u201d is inconsistent with coordinate system axes. IndexOutOfBounds_1 = Index {0} is out of bounds. @@ -190,6 +189,7 @@ UnexpectedCharactersAtBound_4 = Text for \u2018{0}\u2019 was expected to {1, UnexpectedEndOfFile_1 = Unexpected end of file while reading \u201c{0}\u201d. UnexpectedEndOfString_1 = More characters were expected at the end of \u201c{0}\u201d. UnexpectedFileFormat_2 = File \u201c{1}\u201d seems to be encoded in an other format than {0}. +UnexpectedNamespace_2 = The \u201c{1}\u201d name is not valid in this context, because the \u201c{0}\u201d namespace was expected. UnexpectedParameter_1 = Parameter \u201c{0}\u201d was not expected. UnexpectedProperty_2 = Property \u201c{1}\u201d was not expected in \u201c{0}\u201d. UnexpectedScaleFactorForUnit_2 = Unexpected scale factor {1,number} for unit of measurement \u201c{0}\u201d. diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties index 673dd4df77..305e55dfd6 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties +++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties @@ -103,7 +103,6 @@ IncompatibleUnit_1 = L\u2019unit\u00e9 \u00ab\u202f{0}\u202f\u00b IncompatibleUnits_2 = Les unit\u00e9s \u00ab\u202f{0}\u202f\u00bb et \u00ab\u202f{1}\u202f\u00bb ne sont pas compatibles. IncompatibleUnitDimension_5 = L\u2019unit\u00e9 de mesure \u00ab\u202f{0}\u202f\u00bb a la dimension de \u2018{1}\u2019 ({2}). Elle est incompatible avec la dimension de \u2018{3}\u2019 ({4}). InconsistentAttribute_2 = La valeur \u00ab\u202f{1}\u202f\u00bb de l\u2019attribut \u2018{0}\u2019 n\u2019est pas coh\u00e9rente avec celles des autres attributs. -InconsistentNamespace_2 = L\u2019espace de nom \u201c{0}\u201d \u00e9tait attendu pour \u201c{1}\u201d. InconsistentTableColumns = Les colonnes des tables ne sont pas coh\u00e9rentes. InconsistentUnitsForCS_1 = L\u2019unit\u00e9 de mesure \u00ab\u202f{0}\u202f\u00bb n\u2019est pas coh\u00e9rente avec les axes du syst\u00e8me de coordonn\u00e9es. IndexOutOfBounds_1 = L\u2019index {0} est en dehors des limites permises. @@ -186,6 +185,7 @@ UnexpectedCharactersAtBound_4 = Le texte pour \u2018{0}\u2019 devait {1,choi UnexpectedEndOfFile_1 = Fin de fichier inattendue lors de la lecture de \u00ab\u202f{0}\u202f\u00bb. UnexpectedEndOfString_1 = D\u2019autres caract\u00e8res \u00e9taient attendus \u00e0 la fin du texte \u00ab\u202f{0}\u202f\u00bb. UnexpectedFileFormat_2 = Le fichier \u00ab\u202f{1}\u202f\u00bb semble \u00eatre encod\u00e9 dans un autre format que {0}. +UnexpectedNamespace_2 = Le nom \u201c{1}\u201d n\u2019est pas valide dans ce contexte, parce que l\u2019espace de nom \u201c{0}\u201d \u00e9tait attendu. UnexpectedParameter_1 = Le param\u00e8tre \u00ab\u202f{0}\u202f\u00bb est inattendu. UnexpectedProperty_2 = La propri\u00e9t\u00e9 \u00ab\u202f{1}\u202f\u00bb est inattendue dans \u00ab\u202f{0}\u202f\u00bb. UnexpectedScaleFactorForUnit_2 = Le facteur d\u2019\u00e9chelle {1,number} est inattendu pour l\u2019unit\u00e9 de mesure \u00ab\u202f{0}\u202f\u00bb. diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java index 81b0b121cf..6995585ad3 100644 --- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java +++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java @@ -25,9 +25,6 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import org.apache.sis.util.ArgumentChecks; -import org.apache.sis.util.NullArgumentException; -import org.apache.sis.util.iso.Names; import org.opengis.util.NameSpace; import org.opengis.util.NameFactory; import org.opengis.util.GenericName; @@ -59,7 +56,9 @@ import org.apache.sis.metadata.iso.DefaultMetadata; import org.apache.sis.metadata.sql.MetadataStoreException; import org.apache.sis.util.collection.BackingStoreException; import org.apache.sis.util.collection.TreeTable; +import org.apache.sis.util.iso.DefaultNameSpace; import org.apache.sis.util.resources.Errors; +import org.apache.sis.util.ArgumentChecks; /** @@ -68,6 +67,7 @@ import org.apache.sis.util.resources.Errors; * @author Rémi Maréchal (Geomatys) * @author Martin Desruisseaux (Geomatys) * @author Thi Phuong Hao Nguyen (VNSC) + * @author Alexis Manin (Geomatys) * @version 1.3 * @since 0.8 * @module @@ -469,6 +469,7 @@ public class GeoTiffStore extends DataStore implements Aggregate { /** * Returns the image at the given index. Images numbering starts at 1. + * If the given string has a scope (e.g. "filename:1"), then the scope * * @param sequence string representation of the image index, starting at 1. * @return image at the given index. @@ -476,50 +477,50 @@ public class GeoTiffStore extends DataStore implements Aggregate { */ @Override public synchronized GridCoverageResource findResource(final String sequence) throws DataStoreException { - try { - final int index = parseIndex(sequence); + ArgumentChecks.ensureNonNull("sequence", sequence); + final int index = parseImageIndex(sequence); + if (index >= 0) try { final GridCoverageResource image = reader().getImage(index - 1); if (image != null) return image; - } catch (NullArgumentException | IllegalArgumentException e) { - throw new IllegalNameException(StoreUtilities.resourceNotFound(this, sequence), e); } catch (IOException e) { throw errorIO(e); } - throw new IllegalNameException(StoreUtilities.resourceNotFound(this, sequence)); } /** - * Validate input resource name and extract the image index it should contain. - * We verify that: + * Validates input resource name and extracts the image index it should contain. + * The resource name may be of the form "1" or "filename:1". We verify that: + * * <ul> - * <li>Input tip (last name part) is a strictly positive integer ({@code > 0})</li> - * <li> - * If input provide more than a tip, i.e has more than one name part, - * we verify that the part just before the tip matches this datastore namespace - * (should be the name of the Geotiff file without its extension). - * </li> + * <li>Input tip (last name part) is a parsable integer.</li> + * <li>If input provides more than a tip, all test before the tip matches this datastore namespace + * (should be the name of the Geotiff file without its extension).</li> * </ul> * - * @param resourceName A string representing the name of a resource present in this datastore. - * @return The index of the Geotiff image matching the requested resource. - * @throws IllegalArgumentException If input validation fails. + * @param sequence a string representing the name of a resource present in this datastore. + * @return the index of the Geotiff image matching the requested resource. + * There is no verification that the returned index is valid. + * @throws IllegalNameException if the argument use an invalid namespace or if the tip is not an integer. */ - private int parseIndex(String resourceName) { - final GenericName name = Names.parseGenericName(null, null, resourceName); - final String tip = name.tip().toString(); - ArgumentChecks.ensureNonEmpty("Resource name tip", tip); - - final int depth = name.depth(); - if (depth > 1) { - final GenericName userNameSpace = name.getParsedNames().get(depth - 2); - final GenericName storeNameSpace = namespace().name(); - if (!userNameSpace.equals(storeNameSpace)) throw new IllegalArgumentException( - "Input name head does not match this datastore context. Expected: " + storeNameSpace + " but received: " + userNameSpace); + private int parseImageIndex(String sequence) throws IllegalNameException { + final NameSpace namespace = namespace(); + final String separator = DefaultNameSpace.getSeparator(namespace, false); + final int s = sequence.lastIndexOf(separator); + if (s >= 0) { + if (namespace != null) { + final String expected = namespace.name().toString(); + if (!sequence.substring(0, s).equals(expected)) { + throw new IllegalNameException(errors().getString(Errors.Keys.UnexpectedNamespace_2, expected, sequence)); + } + } + sequence = sequence.substring(s + separator.length()); + } + try { + return Integer.parseInt(sequence); + } catch (NumberFormatException e) { + throw new IllegalNameException(StoreUtilities.resourceNotFound(this, sequence), e); } - final int index = Integer.parseInt(tip); - ArgumentChecks.ensureStrictlyPositive("Resource index", index); - return index; } /** diff --git a/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/SelfConsistencyTest.java b/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/SelfConsistencyTest.java index 333402ae8a..48ad7fb6ce 100644 --- a/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/SelfConsistencyTest.java +++ b/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/SelfConsistencyTest.java @@ -19,6 +19,7 @@ package org.apache.sis.storage.geotiff; import java.util.List; import java.util.Optional; import java.nio.file.Path; +import org.opengis.util.GenericName; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.GridCoverageResource; import org.apache.sis.storage.IllegalNameException; @@ -26,12 +27,10 @@ import org.apache.sis.storage.StorageConnector; import org.apache.sis.test.OptionalTestData; import org.apache.sis.test.storage.CoverageReadConsistency; import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Test; -import org.opengis.util.GenericName; +import static org.junit.Assert.*; import static org.junit.Assume.assumeNotNull; @@ -43,7 +42,8 @@ import static org.junit.Assume.assumeNotNull; * a subset of data. * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @author Alexis Manin (Geomatys) + * @version 1.3 * @since 1.1 * @module */ @@ -90,24 +90,31 @@ public final strictfp class SelfConsistencyTest extends CoverageReadConsistency super(store.components().iterator().next()); } + /** + * Verifies that {@link GeoTiffStore#findResource(String)} returns the resource when using + * either the full name or only its tip. + * + * @throws DataStoreException if an error occurred while reading the file. + */ @Test - public void findResourceByName() throws Exception { + public void findResourceByName() throws DataStoreException { final List<GridCoverageResource> datasets = store.components(); - Assume.assumeFalse(datasets.isEmpty()); + assertFalse(datasets.isEmpty()); for (GridCoverageResource dataset : datasets) { final GenericName name = dataset.getIdentifier() .orElseThrow(() -> new AssertionError("A component of the GeoTiff datastore is unnamed")); GridCoverageResource foundResource = store.findResource(name.toString()); - Assert.assertEquals(dataset, foundResource); + assertSame(dataset, foundResource); foundResource = store.findResource(name.tip().toString()); - Assert.assertEquals(dataset, foundResource); + assertSame(dataset, foundResource); } - try { final GridCoverageResource r = store.findResource("a_wrong_namespace:1"); - Assert.fail("No dataset should be returned when user specify the wrong namespace. However, the datastore returned "+ r); + fail("No dataset should be returned when user specify the wrong namespace. However, the datastore returned " + r); } catch (IllegalNameException e) { - // Expected behaviour + // Expected behaviour. + final String message = e.getMessage(); + assertTrue(message, message.contains("a_wrong_namespace:1")); } } }