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 9e5697263de0940bb3567249f0fa7424d1215f71
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Wed May 4 18:59:15 2022 +0200

    If the World File uses a format which is known to support only one image 
(PNG, BMP, WBMP and maybe JPEG),
    returns a data store which implements directly `GridCoverageResource` 
instead of `Aggregate`.
---
 .../sis/internal/storage/image/FormatFilter.java   |  19 +-
 .../sis/internal/storage/image/FormatFinder.java   | 288 +++++++++++++++++++++
 .../internal/storage/image/MultiImageStore.java    |  62 +++++
 .../internal/storage/image/SingleImageStore.java   | 191 ++++++++++++++
 .../internal/storage/image/WorldFileResource.java  |   5 +-
 .../sis/internal/storage/image/WorldFileStore.java | 116 +++------
 .../storage/image/WorldFileStoreProvider.java      |  67 +++--
 .../sis/internal/storage/image/WritableStore.java  |  81 ++----
 .../storage/image/SelfConsistencyTest.java         |   2 +-
 .../internal/storage/image/WorldFileStoreTest.java |  12 +-
 10 files changed, 681 insertions(+), 162 deletions(-)

diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/FormatFilter.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/FormatFilter.java
index ceda08a109..0d1056b864 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/FormatFilter.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/FormatFilter.java
@@ -168,14 +168,15 @@ enum FormatFilter {
      * This is intentionally not done automatically by {@link 
StorageConnector}.
      *
      * @param  identifier  the property value to use as a filtering criterion, 
or {@code null} if none.
-     * @param  connector   provider of the input to be given to the new reader 
instance.
+     * @param  format      provider of the input to be given to the new reader 
instance.
      * @param  deferred    initially empty map to be populated with providers 
tested by this method.
      * @return the new image reader instance with its input initialized, or 
{@code null} if none was found.
      * @throws DataStoreException if an error occurred while opening a stream 
from the storage connector.
      * @throws IOException if an error occurred while creating the image 
reader instance.
      */
-    final ImageReader createReader(final String identifier, final 
StorageConnector connector,
-                                   final Map<ImageReaderSpi,Boolean> deferred) 
throws IOException, DataStoreException
+    final ImageReader createReader(final String identifier, final FormatFinder 
format,
+                                   final Map<ImageReaderSpi,Boolean> deferred)
+            throws IOException, DataStoreException
     {
         final Iterator<ImageReaderSpi> it = 
getServiceProviders(ImageReaderSpi.class, identifier);
         while (it.hasNext()) {
@@ -183,12 +184,12 @@ enum FormatFilter {
             if (deferred.putIfAbsent(provider, Boolean.FALSE) == null) {
                 for (final Class<?> type : provider.getInputTypes()) {
                     if (ArraysExt.contains(VALID_INPUTS, type)) {
-                        final Object input = connector.getStorageAs(type);
+                        final Object input = 
format.connector.getStorageAs(type);
                         if (input != null) {
                             if (provider.canDecodeInput(input)) {
-                                connector.closeAllExcept(input);
                                 final ImageReader reader = 
provider.createReaderInstance();
                                 reader.setInput(input, false, true);
+                                format.keepOpen = input;
                                 return reader;
                             }
                             break;        // Skip other input types, try the 
next provider.
@@ -209,14 +210,14 @@ enum FormatFilter {
      * This is intentionally not done automatically by {@link 
StorageConnector}.
      *
      * @param  identifier  the property value to use as a filtering criterion, 
or {@code null} if none.
-     * @param  output      the output to be given to the new reader instance.
+     * @param  format      provider of the output to be given to the new 
writer instance.
      * @param  image       the image to write, or {@code null} if unknown.
      * @param  deferred    initially empty map to be populated with providers 
tested by this method.
      * @return the new image writer instance with its output initialized, or 
{@code null} if none was found.
      * @throws DataStoreException if an error occurred while opening a stream 
from the storage connector.
      * @throws IOException if an error occurred while creating the image 
writer instance.
      */
-    final ImageWriter createWriter(final String identifier, final 
StorageConnector connector, final RenderedImage image,
+    final ImageWriter createWriter(final String identifier, final FormatFinder 
format, final RenderedImage image,
                                    final Map<ImageWriterSpi,Boolean> deferred) 
throws IOException, DataStoreException
     {
         final Iterator<ImageWriterSpi> it = 
getServiceProviders(ImageWriterSpi.class, identifier);
@@ -226,11 +227,11 @@ enum FormatFilter {
                 if (image == null || provider.canEncodeImage(image)) {
                     for (final Class<?> type : provider.getOutputTypes()) {
                         if (ArraysExt.contains(VALID_OUTPUTS, type)) {
-                            final Object output = connector.getStorageAs(type);
+                            final Object output = 
format.connector.getStorageAs(type);
                             if (output != null) {
-                                connector.closeAllExcept(output);
                                 final ImageWriter writer = 
provider.createWriterInstance();
                                 writer.setOutput(output);
+                                format.keepOpen = output;
                                 return writer;
                             }
                         }
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/FormatFinder.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/FormatFinder.java
new file mode 100644
index 0000000000..d4f7f83837
--- /dev/null
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/FormatFinder.java
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.storage.image;
+
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.io.DataOutput;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Files;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.ImageWriter;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.spi.ImageWriterSpi;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.ImageOutputStream;
+import javax.imageio.stream.FileImageOutputStream;
+import org.apache.sis.storage.StorageConnector;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.internal.storage.io.IOUtilities;
+
+
+/**
+ * Helper class for finding the {@link ImageReader} or {@link ImageWriter} 
instance to use.
+ * This is a temporary object used only at {@link WorldFileStore} construction 
time.
+ * It also helps to choose which {@link WorldFileStore} subclass to 
instantiate.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since   1.2
+ * @module
+ */
+final class FormatFinder implements AutoCloseable {
+    /**
+     * The factory that created this {@code DataStore} instance, or {@code 
null} if unspecified.
+     */
+    final WorldFileStoreProvider provider;
+
+    /**
+     * Information about the storage (URL, stream, <i>etc</i>).
+     */
+    final StorageConnector connector;
+
+    /**
+     * The {@link #connector} object to keep open if we successfully created a 
{@link WorldFileStore}.
+     * This is often the same object than {@link #storage} but may be 
different if an {@link ImageInputStream}
+     * has been created from the storage object.
+     *
+     * <p>This value is {@code null} until successful instantiation of image 
reader or writer.
+     * A null value means to close everything, which is the desired behavior 
in case of failure.</p>
+     */
+    Object keepOpen;
+
+    /**
+     * The file, URL or stream where to read or write the image.
+     * If the {@linkplain StorageConnector#getStorage() user-specified 
storage} was an {@link ImageReader}
+     * or {@link ImageWriter}, then the value stored in this field is the 
input/output of the reader/writer.
+     */
+    final Object storage;
+
+    /**
+     * The image reader if specified or created by this {@code FormatFinder}, 
or {@code null}.
+     */
+    private ImageReader reader;
+
+    /**
+     * The image writer if specified or created by this {@code FormatFinder}, 
or {@code null}.
+     */
+    private ImageWriter writer;
+
+    /**
+     * Whether we already made an attempt to find the image reader or writer 
using {@link ImageIO} registry.
+     */
+    private boolean readerLookupDone, writerLookupDone;
+
+    /**
+     * {@code true} if the {@linkplain #storage} seems to be writable.
+     */
+    final boolean isWritable;
+
+    /**
+     * {@code true} if the storage should be open is write mode instead of 
read mode.
+     * This is {@code true} if the file does not exist or the file is empty.
+     */
+    final boolean openAsWriter;
+
+    /**
+     * {@code true} if the file is known to be empty, or {@code false} in case 
of doubt.
+     */
+    final boolean fileIsEmpty;
+
+    /**
+     * The filename extension (may be an empty string), or {@code null} if 
unknown.
+     * It does not include the leading dot.
+     */
+    final String suffix;
+
+    /**
+     * Creates a new format finder.
+     *
+     * @param  provider   the factory that created this {@code DataStore} 
instance, or {@code null} if unspecified.
+     * @param  connector  information about the storage (URL, stream, 
<i>etc</i>).
+     */
+    FormatFinder(final WorldFileStoreProvider provider, final StorageConnector 
connector)
+            throws DataStoreException, IOException
+    {
+        this.provider  = provider;
+        this.connector = connector;
+        Object storage = connector.getStorage();
+        if (storage instanceof ImageReader) {
+            reader  = (ImageReader) storage;
+            storage = reader.getInput();
+            readerLookupDone = true;
+        } else if (storage instanceof ImageWriter) {
+            writer  = (ImageWriter) storage;
+            storage = writer.getOutput();
+            writerLookupDone = true;
+        }
+        this.storage = storage;
+        this.suffix  = IOUtilities.extension(storage);
+        /*
+         * Detect if the image can be opened in read/write mode.
+         * If not, it will be opened in read-only mode.
+         */
+        if (writer != null) {
+            isWritable   = true;
+            openAsWriter = true;
+            fileIsEmpty  = false;
+        } else if (reader != null) {
+            isWritable   = (reader.getInput() instanceof DataOutput);         
// Parent of ImageOutputStream.
+            openAsWriter = false;
+            fileIsEmpty  = false;
+        } else {
+            isWritable = WorldFileStoreProvider.isWritable(connector);
+            if (isWritable) {
+                final Path path = connector.getStorageAs(Path.class);
+                if (path != null) {
+                    fileIsEmpty  = !Files.exists(path) || Files.size(path) == 
0;
+                    openAsWriter = fileIsEmpty;
+                    return;
+                }
+            }
+            openAsWriter = false;
+            fileIsEmpty  = false;
+        }
+    }
+
+    /**
+     * Returns the name of the format.
+     *
+     * @return name of the format, or {@code null} if unknown.
+     */
+    final String[] getFormatName() throws DataStoreException, IOException {
+        if (openAsWriter) {
+            final ImageWriter writer = getOrCreateWriter();
+            if (writer != null) {
+                final ImageWriterSpi spi = writer.getOriginatingProvider();
+                if (spi != null) {
+                    return spi.getFormatNames();
+                }
+            }
+        } else {
+            final ImageReader reader = getOrCreateReader();
+            if (reader != null) {
+                final ImageReaderSpi spi = reader.getOriginatingProvider();
+                if (spi != null) {
+                    return spi.getFormatNames();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the user-specified reader or searches for a reader that claim 
to be able to read the storage input.
+     * This method tries first the readers associated to the file suffix. If 
no reader is found, then this method
+     * tries all other readers.
+     *
+     * @return the reader, or {@code null} if none could be found.
+     */
+    final ImageReader getOrCreateReader() throws DataStoreException, 
IOException {
+        if (!readerLookupDone) {
+            readerLookupDone = true;
+            final Map<ImageReaderSpi,Boolean> deferred = new LinkedHashMap<>();
+            if (suffix != null) {
+                reader = FormatFilter.SUFFIX.createReader(suffix, this, 
deferred);
+            }
+            if (reader == null) {
+                reader = FormatFilter.SUFFIX.createReader(null, this, 
deferred);
+                if (reader == null) {
+                    /*
+                     * If no reader has been found, maybe `StorageConnector` 
has not been able to create
+                     * an `ImageInputStream`. It may happen if the storage 
object is of unknown type.
+                     * Check if it is the case, then try all providers that we 
couldn't try because of that.
+                     */
+                    ImageInputStream stream = null;
+                    for (final Map.Entry<ImageReaderSpi,Boolean> entry : 
deferred.entrySet()) {
+                        if (entry.getValue()) {
+                            if (stream == null) {
+                                if (isWritable) {
+                                    // ImageOutputStream is both read and 
write.
+                                    stream = 
ImageIO.createImageOutputStream(storage);
+                                }
+                                if (stream == null) {
+                                    stream = 
ImageIO.createImageInputStream(storage);
+                                    if (stream == null) break;
+                                }
+                            }
+                            final ImageReaderSpi p = entry.getKey();
+                            if (p.canDecodeInput(stream)) {
+                                reader = p.createReaderInstance();
+                                reader.setInput(stream);
+                                keepOpen = storage;
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return reader;
+    }
+
+    /**
+     * Returns the user-specified writer or searches for a writer for the file 
suffix.
+     *
+     * @return the writer, or {@code null} if none could be found.
+     */
+    final ImageWriter getOrCreateWriter() throws DataStoreException, 
IOException {
+        if (!writerLookupDone) {
+            writerLookupDone = true;
+            final Map<ImageWriterSpi,Boolean> deferred = new LinkedHashMap<>();
+            if (suffix != null) {
+                writer = FormatFilter.SUFFIX.createWriter(suffix, this, null, 
deferred);
+            }
+            if (writer == null) {
+                writer = FormatFilter.SUFFIX.createWriter(null, this, null, 
deferred);
+                if (writer == null) {
+                    ImageOutputStream stream = null;
+                    for (final Map.Entry<ImageWriterSpi,Boolean> entry : 
deferred.entrySet()) {
+                        if (entry.getValue()) {
+                            if (stream == null) {
+                                final File file = 
connector.getStorageAs(File.class);
+                                if (file != null) {
+                                    stream = new FileImageOutputStream(file);
+                                } else {
+                                    stream = 
ImageIO.createImageOutputStream(storage);
+                                    if (stream == null) break;
+                                }
+                            }
+                            final ImageWriterSpi p = entry.getKey();
+                            writer = p.createWriterInstance();
+                            writer.setOutput(stream);
+                            keepOpen = storage;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        return writer;
+    }
+
+    /**
+     * Closes all unused resources. Keep open only the find of objects needed 
by the image reader or writer.
+     * This method must be invoked after by {@link WorldFileStore} 
construction.
+     */
+    @Override
+    public final void close() throws DataStoreException {
+        connector.closeAllExcept(keepOpen);
+    }
+}
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/MultiImageStore.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/MultiImageStore.java
new file mode 100644
index 0000000000..5609c1ebd6
--- /dev/null
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/MultiImageStore.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.storage.image;
+
+import java.io.IOException;
+import org.apache.sis.storage.Aggregate;
+import org.apache.sis.storage.WritableAggregate;
+import org.apache.sis.storage.DataStoreException;
+
+
+/**
+ * A world file store exposing in the public API the fact that it is an 
aggregate.
+ * This class is used for image formats that may store many images per file.
+ * Examples: TIFF and GIF image formats.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since   1.2
+ * @module
+ */
+final class MultiImageStore extends WorldFileStore implements Aggregate {
+    /**
+     * Creates a new store from the given file, URL or stream.
+     *
+     * @param  format  information about the storage (URL, stream, <i>etc</i>) 
and the reader/writer to use.
+     * @throws DataStoreException if an error occurred while opening the 
stream.
+     * @throws IOException if an error occurred while creating the image 
reader instance.
+     */
+    MultiImageStore(final FormatFinder format) throws DataStoreException, 
IOException {
+        super(format, false);
+    }
+
+    /**
+     * The writable variant of {@link MultiImageStore}.
+     */
+    static final class Writable extends WritableStore implements 
WritableAggregate {
+        /**
+         * Creates a new store from the given file, URL or stream.
+         *
+         * @param  format  information about the storage (URL, stream, 
<i>etc</i>) and the reader/writer to use.
+         * @throws DataStoreException if an error occurred while opening the 
stream.
+         * @throws IOException if an error occurred while creating the image 
reader instance.
+         */
+        Writable(final FormatFinder format) throws DataStoreException, 
IOException {
+            super(format);
+        }
+    }
+}
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/SingleImageStore.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/SingleImageStore.java
new file mode 100644
index 0000000000..1c540ca55e
--- /dev/null
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/SingleImageStore.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.storage.image;
+
+import java.util.List;
+import java.util.Optional;
+import java.io.IOException;
+import org.opengis.geometry.Envelope;
+import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.coverage.grid.GridCoverage;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.storage.RasterLoadingStrategy;
+import org.apache.sis.storage.UnsupportedQueryException;
+import org.apache.sis.storage.WritableGridCoverageResource;
+import org.apache.sis.storage.Query;
+
+
+/**
+ * A world file store which is expected to contain exactly one image.
+ * This class is used for image formats that are restricted to one image per 
file.
+ * Examples: PNG and BMP image formats.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since   1.2
+ * @module
+ */
+class SingleImageStore extends WorldFileStore implements GridCoverageResource {
+    /**
+     * The singleton resource in this aggregate. Fetched when first needed.
+     */
+    private volatile WorldFileResource delegate;
+
+    /**
+     * Creates a new store from the given file, URL or stream.
+     *
+     * @param  format  information about the storage (URL, stream, <i>etc</i>) 
and the reader/writer to use.
+     * @throws DataStoreException if an error occurred while opening the 
stream.
+     * @throws IOException if an error occurred while creating the image 
reader instance.
+     */
+    SingleImageStore(final FormatFinder format) throws DataStoreException, 
IOException {
+        super(format, false);
+    }
+
+    /**
+     * Returns {@code true} for meaning that the singleton component will be 
used only for internal purposes.
+     */
+    @Override
+    final boolean isComponentHidden() {
+        return true;
+    }
+
+    /**
+     * Returns the singleton resource in this aggregate. The delegate is used 
for all
+     * {@link GridCoverageResource} operations but <strong>not</strong> for 
the following operations:
+     *
+     * <ul>
+     *   <li>{@link #getIdentifier()} because we want the filename without 
":1" suffix (the image index).</li>
+     *   <li>{@link #getMetadata()} because it is richer than {@link 
WorldFileResource#getMetadata()}.</li>
+     * </ul>
+     */
+    final WorldFileResource delegate() throws DataStoreException {
+        WorldFileResource r = delegate;
+        if (r == null) {
+            delegate = r = ((Components) components()).get(MAIN_IMAGE);
+        }
+        return r;
+    }
+
+    /**
+     * Returns the valid extent of grid coordinates together with the 
conversion from those grid coordinates
+     * to real world coordinates. The CRS and "pixels to CRS" conversion may 
be unknown if the {@code *.prj}
+     * and/or world auxiliary file has not been found.
+     */
+    @Override
+    public final GridGeometry getGridGeometry() throws DataStoreException {
+        return delegate().getGridGeometry();
+    }
+
+    /**
+     * Returns the envelope of the grid geometry if known.
+     * The envelope is absent if the grid geometry does not provide this 
information.
+     */
+    @Override
+    public final Optional<Envelope> getEnvelope() throws DataStoreException {
+        return delegate().getEnvelope();
+    }
+
+    /**
+     * Returns the preferred resolutions (in units of CRS axes) for read 
operations in this data store.
+     */
+    @Override
+    public final List<double[]> getResolutions() throws DataStoreException {
+        return delegate().getResolutions();
+    }
+
+    /**
+     * Returns the ranges of sample values in each band. Those sample 
dimensions describe colors
+     * because the World File format does not provide more information.
+     */
+    @Override
+    public final List<SampleDimension> getSampleDimensions() throws 
DataStoreException {
+        return delegate().getSampleDimensions();
+    }
+
+    /**
+     * Requests a subset of the coverage.
+     */
+    @Override
+    public final GridCoverageResource subset(Query query) throws 
UnsupportedQueryException, DataStoreException {
+        return delegate().subset(query);
+    }
+
+    /**
+     * Loads a subset of the image wrapped by this resource.
+     *
+     * @param  domain  desired grid extent and resolution, or {@code null} for 
reading the whole domain.
+     * @param  range   0-based indices of sample dimensions to read, or {@code 
null} or an empty sequence for reading them all.
+     * @return the grid coverage for the specified domain and range.
+     */
+    @Override
+    public final GridCoverage read(GridGeometry domain, int... range) throws 
DataStoreException {
+        return delegate().read(domain, range);
+    }
+
+    /**
+     * Returns an indication about when the "physical" loading of raster data 
will happen.
+     */
+    @Override
+    public final RasterLoadingStrategy getLoadingStrategy() throws 
DataStoreException {
+        return delegate().getLoadingStrategy();
+    }
+
+    /**
+     * Sets the preferred strategy about when to do the "physical" loading of 
raster data.
+     * Implementations are free to ignore this parameter or to replace the 
given strategy
+     * by the closest alternative that this resource can support.
+     *
+     * @param  strategy  the desired strategy for loading raster data.
+     * @return {@code true} if the given strategy has been accepted, or {@code 
false}
+     *         if this implementation replaced the given strategy by an 
alternative.
+     */
+    @Override
+    public final boolean setLoadingStrategy(RasterLoadingStrategy strategy) 
throws DataStoreException {
+        return delegate().setLoadingStrategy(strategy);
+    }
+
+    /**
+     * The writable variant of {@link MultiImageStore}.
+     */
+    static final class Writable extends SingleImageStore implements 
WritableGridCoverageResource {
+        /**
+         * Creates a new store from the given file, URL or stream.
+         *
+         * @param  format  information about the storage (URL, stream, 
<i>etc</i>) and the reader/writer to use.
+         * @throws DataStoreException if an error occurred while opening the 
stream.
+         * @throws IOException if an error occurred while creating the image 
reader instance.
+         */
+        Writable(final FormatFinder format) throws DataStoreException, 
IOException {
+            super(format);
+        }
+
+        /**
+         * Writes a new coverage in the data store for this resource. If a 
coverage already exists for this resource,
+         * then it will be overwritten only if the {@code TRUNCATE} or {@code 
UPDATE} option is specified.
+         *
+         * @param  coverage  new data to write in the data store for this 
resource.
+         * @param  options   configuration of the write operation.
+         */
+        @Override
+        public void write(GridCoverage coverage, Option... options) throws 
DataStoreException {
+            ((WritableResource) delegate()).write(coverage, options);
+        }
+    }
+}
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileResource.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileResource.java
index 05da7f95f6..8c2f9e53ec 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileResource.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileResource.java
@@ -117,7 +117,7 @@ class WorldFileResource extends 
AbstractGridCoverageResource implements StoreRes
     WorldFileResource(final WorldFileStore store, final StoreListeners parent,
                       final int imageIndex, final GridGeometry gridGeometry)
     {
-        super(parent, false);
+        super(parent, store.isComponentHidden());
         this.store        = store;
         this.imageIndex   = imageIndex;
         this.gridGeometry = gridGeometry;
@@ -208,7 +208,8 @@ class WorldFileResource extends 
AbstractGridCoverageResource implements StoreRes
     }
 
     /**
-     * Returns the ranges of sample values.
+     * Returns the ranges of sample values in each band. Those sample 
dimensions describe colors
+     * because the World File format does not provide more information.
      */
     @Override
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileStore.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileStore.java
index 4ff7398186..0d48e9e8ef 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileStore.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileStore.java
@@ -20,7 +20,6 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Map;
 import java.util.HashMap;
-import java.util.LinkedHashMap;
 import java.util.logging.Level;
 import java.io.IOException;
 import java.io.EOFException;
@@ -32,8 +31,6 @@ import java.nio.file.NoSuchFileException;
 import java.nio.file.StandardOpenOption;
 import javax.imageio.ImageIO;
 import javax.imageio.ImageReader;
-import javax.imageio.ImageWriter;
-import javax.imageio.spi.ImageReaderSpi;
 import javax.imageio.stream.ImageInputStream;
 import org.opengis.metadata.Metadata;
 import org.opengis.metadata.maintenance.ScopeCode;
@@ -53,7 +50,6 @@ import org.apache.sis.storage.ReadOnlyStorageException;
 import org.apache.sis.storage.UnsupportedStorageException;
 import org.apache.sis.internal.storage.Resources;
 import org.apache.sis.internal.storage.PRJDataStore;
-import org.apache.sis.internal.storage.io.IOUtilities;
 import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
 import org.apache.sis.internal.storage.MetadataBuilder;
 import org.apache.sis.internal.util.ListOfUnknownSize;
@@ -109,13 +105,16 @@ import org.apache.sis.setup.OptionKey;
  * Because some image formats can store an arbitrary amount of images,
  * this data store is considered as an aggregate with one resource per image.
  * All image should have the same size and all resources will share the same 
{@link GridGeometry}.
+ * However this base class does not implement the {@link Aggregate} interface 
directly in order to
+ * give a chance to subclasses to implement {@link GridCoverageResource} 
directly when the format
+ * is known to support only one image per file.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.2
  * @since   1.2
  * @module
  */
-class WorldFileStore extends PRJDataStore implements Aggregate {
+class WorldFileStore extends PRJDataStore {
     /**
      * Image I/O format names (ignoring case) for which we have an entry in 
the {@code SpatialMetadata} database.
      */
@@ -217,84 +216,45 @@ class WorldFileStore extends PRJDataStore implements 
Aggregate {
      *
      * @param  provider   the factory that created this {@code DataStore} 
instance, or {@code null} if unspecified.
      * @param  connector  information about the storage (URL, stream, 
<i>etc</i>).
-     * @param  readOnly   whether to fail if the channel can not be opened at 
least in read mode.
      * @throws DataStoreException if an error occurred while opening the 
stream.
      * @throws IOException if an error occurred while creating the image 
reader instance.
      */
-    WorldFileStore(final WorldFileStoreProvider provider, final 
StorageConnector connector, final boolean readOnly)
+    public WorldFileStore(final WorldFileStoreProvider provider, final 
StorageConnector connector)
             throws DataStoreException, IOException
     {
-        super(provider, connector);
-        identifiers = new HashMap<>();
+        this(new FormatFinder(provider, connector), true);
+    }
+
+    /**
+     * Creates a new store from the given file, URL or stream.
+     *
+     * @param  format    information about the storage (URL, stream, 
<i>etc</i>) and the reader/writer to use.
+     * @param  readOnly  {@code true} if the store should be open in read-only 
mode, ignoring {@code format}.
+     *                   This is a workaround for RFE #4093999 in Sun's bug 
database, for allowing us to invoke
+     *                   {@link FormatFinder#cleanup()} when invoked from the 
public constructor.
+     * @throws DataStoreException if an error occurred while opening the 
stream.
+     * @throws IOException if an error occurred while creating the image 
reader instance.
+     */
+    WorldFileStore(final FormatFinder format, final boolean readOnly) throws 
DataStoreException, IOException {
+        super(format.provider, format.connector);
         listeners.useWarningEventsOnly();
-        final Object storage = connector.getStorage();
-        if (storage instanceof ImageReader) {
-            reader = (ImageReader) storage;
-            suffix = IOUtilities.extension(reader.getInput());
+        identifiers = new HashMap<>();
+        suffix = format.suffix;
+        if (readOnly || !format.openAsWriter) {
+            reader = format.getOrCreateReader();
+            if (reader == null) {
+                throw new UnsupportedStorageException(super.getLocale(), 
WorldFileStoreProvider.NAME,
+                            format.storage, 
format.connector.getOption(OptionKey.OPEN_OPTIONS));
+            }
             configureReader();
-            return;
-        }
-        if (storage instanceof ImageWriter) {
-            suffix = IOUtilities.extension(((ImageWriter) 
storage).getOutput());
-            return;
-        }
-        suffix = IOUtilities.extension(storage);
-        if (!(readOnly || fileExists(connector))) {
+            if (readOnly) {
+                format.close();
+            }
             /*
-             * If the store is opened in read-write mode, create the image 
reader only
-             * if the file exists and is non-empty. Otherwise we let `reader` 
to null
-             * and the caller will create an image writer instead.
+             * Do not invoke any method that may cause the image reader to 
start reading the stream,
+             * because the `WritableStore` subclass will want to save the 
initial stream position.
              */
-            return;
         }
-        /*
-         * Search for a reader that claim to be able to read the storage input.
-         * First we try readers associated to the file suffix. If no reader is
-         * found, we try all other readers.
-         */
-        final Map<ImageReaderSpi,Boolean> deferred = new LinkedHashMap<>();
-        if (suffix != null) {
-            reader = FormatFilter.SUFFIX.createReader(suffix, connector, 
deferred);
-        }
-        if (reader == null) {
-            reader = FormatFilter.SUFFIX.createReader(null, connector, 
deferred);
-fallback:   if (reader == null) {
-                /*
-                 * If no reader has been found, maybe `StorageConnector` has 
not been able to create
-                 * an `ImageInputStream`. It may happen if the storage object 
is of unknown type.
-                 * Check if it is the case, then try all providers that we 
couldn't try because of that.
-                 */
-                ImageInputStream stream = null;
-                for (final Map.Entry<ImageReaderSpi,Boolean> entry : 
deferred.entrySet()) {
-                    if (entry.getValue()) {
-                        if (stream == null) {
-                            if (!readOnly) {
-                                // ImageOutputStream is both read and write.
-                                stream = 
ImageIO.createImageOutputStream(storage);
-                            }
-                            if (stream == null) {
-                                stream = 
ImageIO.createImageInputStream(storage);
-                                if (stream == null) break;
-                            }
-                        }
-                        final ImageReaderSpi p = entry.getKey();
-                        if (p.canDecodeInput(stream)) {
-                            connector.closeAllExcept(storage);
-                            reader = p.createReaderInstance();
-                            reader.setInput(stream);
-                            break fallback;
-                        }
-                    }
-                }
-                throw new UnsupportedStorageException(super.getLocale(), 
WorldFileStoreProvider.NAME,
-                            storage, 
connector.getOption(OptionKey.OPEN_OPTIONS));
-            }
-        }
-        configureReader();
-        /*
-         * Do not invoke any method that may cause the image reader to start 
reading the stream,
-         * because the `WritableStore` subclass will want to save the initial 
stream position.
-         */
     }
 
     /**
@@ -567,7 +527,6 @@ loop:   for (int convention=0;; convention++) {
      *
      * @return list of images in this store.
      */
-    @Override
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
     public final synchronized Collection<? extends GridCoverageResource> 
components() throws DataStoreException {
         if (components == null) try {
@@ -762,6 +721,15 @@ loop:   for (int convention=0;; convention++) {
         return new WorldFileResource(this, listeners, index, 
getGridGeometry(index));
     }
 
+    /**
+     * Whether the component of this data store is used only as a delegate.
+     * This is {@code false} when the components will be given to the user,
+     * or {@code true} if the singleton component will be used only for 
internal purposes.
+     */
+    boolean isComponentHidden() {
+        return false;
+    }
+
     /**
      * Prepares an image reader compatible with the writer and sets its input.
      * This method is invoked for switching from write mode to read mode.
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileStoreProvider.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileStoreProvider.java
index 5306122bb2..e9c3959cd8 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileStoreProvider.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileStoreProvider.java
@@ -18,10 +18,7 @@ package org.apache.sis.internal.storage.image;
 
 import java.util.Set;
 import java.util.HashSet;
-import java.io.DataOutput;
 import java.io.IOException;
-import javax.imageio.ImageReader;
-import javax.imageio.ImageWriter;
 import javax.imageio.spi.ImageReaderSpi;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.StorageConnector;
@@ -30,7 +27,9 @@ import org.apache.sis.internal.storage.StoreMetadata;
 import org.apache.sis.internal.storage.PRJDataStore;
 import org.apache.sis.internal.storage.io.IOUtilities;
 import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.storage.Aggregate;
 import org.apache.sis.storage.ProbeResult;
+import org.apache.sis.util.ArraysExt;
 
 
 /**
@@ -51,10 +50,42 @@ public final class WorldFileStoreProvider extends 
PRJDataStore.Provider {
      */
     static final String NAME = "World file";
 
+    /**
+     * Name of image formats that are considered to allow only one image.
+     * There is no public Image I/O API giving this information, so we have to 
use a hard-coded list.
+     * All formats not in this list are assumed to allow more than one image.
+     *
+     * <h4>Case of JPEG</h4>
+     * The JPEG image reader implementation in standard JDK seems to count a 
number of images that can be anything.
+     * However documentation on the web often describes the JPEG format as a 
container for a single image.
+     * It is not clear if we should include JPEG in this list or not.
+     */
+    private static final String[] SINGLE_IMAGE_FORMATS = {"PNG", "BMP", 
"WBMP", "JPEG"};
+
+    /**
+     * Whether the provider is allowed to create {@link GridCoverageResource} 
instances
+     * instead of {@link Aggregate} instances.
+     */
+    private final boolean allowSingleton;
+
     /**
      * Creates a new provider.
      */
     public WorldFileStoreProvider() {
+        allowSingleton = true;
+    }
+
+    /**
+     * Creates a new provider with the given configuration.
+     * If {@code allowSingleton} is {@code false}, then this provider will 
unconditionally create
+     * {@link WorldFileStore} instances that implement the {@link Aggregate} 
interface, regardless
+     * if the image format allows many pictures or not.
+     *
+     * @param allowSingleton  whether the provider is allowed to create {@code 
GridCoverageResource} instances
+     *        instead of {@code Aggregate} instances.
+     */
+    public WorldFileStoreProvider(final boolean allowSingleton) {
+        this.allowSingleton = allowSingleton;
     }
 
     /**
@@ -78,25 +109,29 @@ public final class WorldFileStoreProvider extends 
PRJDataStore.Provider {
      */
     @Override
     public WorldFileStore open(final StorageConnector connector) throws 
DataStoreException {
-        final Object storage = connector.getStorage();
-        boolean isWritable = (storage instanceof ImageWriter);
-        if (!isWritable) {
-            if (storage instanceof ImageReader) {
-                Object input = ((ImageReader) storage).getInput();
-                isWritable = (input instanceof DataOutput);         // Parent 
of ImageOutputStream.
-            } else {
-                isWritable = isWritable(connector);
+        final WorldFileStore store;
+        try (FormatFinder format = new FormatFinder(this, connector)) {
+            boolean isSingleton = false;
+            if (allowSingleton) {
+                final String[] names = format.getFormatName();
+                if (names != null) {
+                    for (final String name : names) {
+                        isSingleton = 
ArraysExt.containsIgnoreCase(SINGLE_IMAGE_FORMATS, name);
+                        if (isSingleton) break;
+                    }
+                }
             }
-        }
-        try {
-            if (isWritable) {
-                return new WritableStore(this, connector);
+            if (format.isWritable) {
+                store = isSingleton ? new SingleImageStore.Writable(format)
+                                    : new  MultiImageStore.Writable(format);
             } else {
-                return new WorldFileStore(this, connector, true);
+                store = isSingleton ? new SingleImageStore(format)
+                                    : new  MultiImageStore(format);
             }
         } catch (IOException e) {
             throw new DataStoreException(e);
         }
+        return store;
     }
 
     /**
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WritableStore.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WritableStore.java
index bd7fe0078c..3d74d0b54e 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WritableStore.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WritableStore.java
@@ -16,10 +16,7 @@
  */
 package org.apache.sis.internal.storage.image;
 
-import java.util.Map;
-import java.util.LinkedHashMap;
 import java.util.function.BiConsumer;
-import java.io.File;
 import java.io.IOException;
 import java.io.BufferedWriter;
 import java.nio.file.StandardOpenOption;
@@ -33,7 +30,6 @@ import javax.imageio.spi.ImageWriterSpi;
 import javax.imageio.spi.ImageReaderWriterSpi;
 import javax.imageio.stream.ImageInputStream;
 import javax.imageio.stream.ImageOutputStream;
-import javax.imageio.stream.FileImageOutputStream;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.GridExtent;
@@ -69,12 +65,20 @@ import org.apache.sis.setup.OptionKey;
  * The image writer will be {@linkplain ImageWriter#dispose() disposed}
  * and its output closed (if {@link AutoCloseable}) when this data store is 
{@linkplain #close() closed}.</p>
  *
+ * <h2>Handling of multi-image files</h2>
+ * Because some image formats can store an arbitrary amount of images,
+ * this data store is considered as an aggregate with one resource per image.
+ * All image should have the same size and all resources will share the same 
{@link GridGeometry}.
+ * However this base class does not implement the {@link WritableAggregate} 
interface directly in order
+ * to give a chance to subclasses to implement {@link GridCoverageResource} 
directly when the format is
+ * known to support only one image per file.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.2
  * @since   1.2
  * @module
  */
-final class WritableStore extends WorldFileStore implements WritableAggregate {
+class WritableStore extends WorldFileStore {
     /**
      * Position of the input/output stream beginning. This is usually 0.
      */
@@ -101,68 +105,28 @@ final class WritableStore extends WorldFileStore 
implements WritableAggregate {
     /**
      * Creates a new store from the given file, URL or stream.
      *
-     * @param  provider   the factory that created this {@code DataStore} 
instance, or {@code null} if unspecified.
-     * @param  connector  information about the storage (URL, stream, 
<i>etc</i>).
+     * @param  format  information about the storage (URL, stream, <i>etc</i>) 
and the reader/writer to use.
      * @throws DataStoreException if an error occurred while opening the 
stream.
      * @throws IOException if an error occurred while creating the image 
reader instance.
      */
-    WritableStore(final WorldFileStoreProvider provider, final 
StorageConnector connector)
-            throws DataStoreException, IOException
-    {
-        super(provider, connector, false);
-        final Object storage = connector.getStorage();
-        final ImageReader reader = getCurrentReader();
-        final Object inout;
-        if (reader != null) {
-            inout = reader.getInput();
-            numImages = -1;
-        } else if (storage instanceof ImageWriter) {
-            writer = (ImageWriter) storage;
-            inout  = writer.getOutput();
-            configureWriter();
+    WritableStore(final FormatFinder format) throws DataStoreException, 
IOException {
+        super(format, false);
+        if (getCurrentReader() != null) {
             numImages = -1;
         } else {
-            /*
-             * If it was possible to initialize an image reader, wait to see 
if an image writer is needed.
-             * Otherwise (i.e. if the destination file does not exist), create 
the image writer immediately.
-             * The code below is a copy of the code in parent class 
constructor (for creating `ImageReader`),
-             * but adapted to the case of creating an `ImageWriter`.
-             */
-            final Map<ImageWriterSpi,Boolean> deferred = new LinkedHashMap<>();
-            if (suffix != null) {
-                writer = FormatFilter.SUFFIX.createWriter(suffix, connector, 
null, deferred);
-            }
+            writer = format.getOrCreateWriter();
             if (writer == null) {
-                writer = FormatFilter.SUFFIX.createWriter(null, connector, 
null, deferred);
-fallback:       if (writer == null) {
-                    ImageOutputStream stream = null;
-                    for (final Map.Entry<ImageWriterSpi,Boolean> entry : 
deferred.entrySet()) {
-                        if (entry.getValue()) {
-                            if (stream == null) {
-                                final File file = 
connector.getStorageAs(File.class);
-                                if (file != null) {
-                                    stream = new FileImageOutputStream(file);
-                                } else {
-                                    stream = 
ImageIO.createImageOutputStream(storage);
-                                    if (stream == null) break;
-                                }
-                            }
-                            final ImageWriterSpi p = entry.getKey();
-                            connector.closeAllExcept(storage);
-                            writer = p.createWriterInstance();
-                            writer.setOutput(stream);
-                            break fallback;
-                        }
-                    }
-                    throw new UnsupportedStorageException(super.getLocale(), 
WorldFileStoreProvider.NAME,
-                                storage, 
connector.getOption(OptionKey.OPEN_OPTIONS));
-                }
+                throw new UnsupportedStorageException(super.getLocale(), 
WorldFileStoreProvider.NAME,
+                            format.storage, 
format.connector.getOption(OptionKey.OPEN_OPTIONS));
             }
             configureWriter();
-            inout = writer.getOutput();
-            // Leave `numImages` to 0 because we know that the stream is empty.
+            if (!format.fileIsEmpty) {
+                numImages = -1;
+            } else {
+                // Leave `numImages` to 0.
+            }
         }
-        streamBeginning = (inout instanceof ImageInputStream) ? 
((ImageInputStream) inout).getStreamPosition() : 0;
+        streamBeginning = (format.storage instanceof ImageInputStream) ? 
((ImageInputStream) format.storage).getStreamPosition() : 0;
     }
 
     /**
@@ -303,7 +267,6 @@ writeCoeffs:    for (int i=0;; i++) {
      * @return the effectively added resource.
      * @throws DataStoreException if the given resource can not be stored in 
this {@code Aggregate}.
      */
-    @Override
     public synchronized Resource add(final Resource resource) throws 
DataStoreException {
         Exception cause = null;
         if (resource instanceof GridCoverageResource) try {
diff --git 
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/image/SelfConsistencyTest.java
 
b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/image/SelfConsistencyTest.java
index 62778d2c60..5da4c06fd3 100644
--- 
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/image/SelfConsistencyTest.java
+++ 
b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/image/SelfConsistencyTest.java
@@ -55,7 +55,7 @@ public final strictfp class SelfConsistencyTest extends 
CoverageReadConsistency
     public static void openFile() throws IOException, DataStoreException {
         final URL url = WorldFileStoreTest.class.getResource("gradient.png");
         assertNotNull("Test file not found.", url);
-        store = new WorldFileStore(null, new StorageConnector(url), true);
+        store = new WorldFileStore(null, new StorageConnector(url));
     }
 
     /**
diff --git 
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/image/WorldFileStoreTest.java
 
b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/image/WorldFileStoreTest.java
index ec967d2d67..9c174d5241 100644
--- 
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/image/WorldFileStoreTest.java
+++ 
b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/image/WorldFileStoreTest.java
@@ -76,7 +76,15 @@ public final strictfp class WorldFileStoreTest extends 
TestCase {
     public void testMetadata() throws DataStoreException {
         final WorldFileStoreProvider provider = new WorldFileStoreProvider();
         try (WorldFileStore store = provider.open(testData())) {
+            /*
+             * Opportunistic check of store type. Should be read-only,
+             * and should have been simplified to the "single image" case.
+             */
             assertFalse(store instanceof WritableStore);
+            assertTrue(store instanceof SingleImageStore);
+            /*
+             * Verify metadata content.
+             */
             assertEquals("gradient", store.getIdentifier().get().toString());
             final Metadata metadata = store.getMetadata();
             final Identification id = 
getSingleton(metadata.getIdentificationInfo());
@@ -98,6 +106,8 @@ public final strictfp class WorldFileStoreTest extends 
TestCase {
 
     /**
      * Tests reading the coverage and writing it in a new file.
+     * This test unconditionally open the data store as an aggregate,
+     * i.e. it bypasses the simplification of PNG files as {@link 
SingleImageStore} view.
      *
      * @throws DataStoreException if an error occurred during Image I/O or 
data store operations.
      * @throws IOException if an error occurred when creating, reading or 
deleting temporary files.
@@ -106,7 +116,7 @@ public final strictfp class WorldFileStoreTest extends 
TestCase {
     public void testReadWrite() throws DataStoreException, IOException {
         final Path directory = Files.createTempDirectory("SIS-");
         try {
-            final WorldFileStoreProvider provider = new 
WorldFileStoreProvider();
+            final WorldFileStoreProvider provider = new 
WorldFileStoreProvider(false);
             try (WorldFileStore source = provider.open(testData())) {
                 assertFalse(source instanceof WritableStore);
                 final GridCoverageResource resource = 
getSingleton(source.components());

Reply via email to