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 b025d4b61f453344f463688695ecb19a2d9079e8
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Wed Apr 6 20:07:33 2022 +0200

    Refactor existing code in preparation for addition of 
`WritableGridCoverageResource` implementations.
---
 .../java/org/apache/sis/image/ImageCombiner.java   |  6 ++--
 .../apache/sis/internal/storage/PRJDataStore.java  |  4 ++-
 .../apache/sis/internal/storage/URIDataStore.java  |  5 ++-
 .../apache/sis/internal/storage/ascii/Store.java   | 19 +++++++++--
 .../sis/internal/storage/io/ChannelDataInput.java  | 13 ++++++++
 .../storage/io/ChannelImageInputStream.java        | 17 +---------
 .../sis/storage/ReadOnlyStorageException.java      |  2 +-
 ...on.java => ResourceAlreadyExistsException.java} | 37 +++++++---------------
 .../sis/storage/WritableGridCoverageResource.java  | 34 ++++++++++++++------
 9 files changed, 76 insertions(+), 61 deletions(-)

diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/image/ImageCombiner.java 
b/core/sis-feature/src/main/java/org/apache/sis/image/ImageCombiner.java
index 533d3acb0f..1ee69e78b9 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ImageCombiner.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ImageCombiner.java
@@ -36,7 +36,7 @@ import org.apache.sis.measure.Units;
 
 /**
  * Combines an arbitrary amount of images into a single one.
- * The combined mages may use different coordinate systems if a resampling 
operation is specified.
+ * The combined images may use different coordinate systems if a resampling 
operation is specified.
  * The workflow is as below:
  *
  * <ol>
@@ -70,8 +70,8 @@ public class ImageCombiner implements Consumer<RenderedImage> 
{
 
     /**
      * Creates an image combiner which will write in the given image. That 
image is not cleared;
-     * pixels that are not overwritten by another image given to {@code 
ImageCombiner} methods
-     * will be left unchanged.
+     * pixels that are not overwritten by calls to the {@code accept(…)} or 
{@code resample(…)}
+     * methods will be left unchanged.
      *
      * @param  destination  the image where to combine images.
      */
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java
index 4c04ec072a..82f8f6556e 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java
@@ -98,8 +98,10 @@ public abstract class PRJDataStore extends URIDataStore {
     protected CoordinateReferenceSystem crs;
 
     /**
-     * Creates a new data store. The following options are recognized:
+     * Creates a new data store. This constructor does not open the file,
+     * so subclass constructors can decide whether to open in read-only or 
read/write mode.
      *
+     * <p>The following options are recognized:</p>
      * <ul>
      *   <li>{@link DataOptionKey#DEFAULT_CRS}: default CRS if no auxiliary 
{@code "*.prj"} file is found.</li>
      *   <li>{@link DataOptionKey#ENCODING}: encoding of the {@code "*.prj"} 
file. Default is the JVM default.</li>
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
index 1f6a701159..8aef150aaa 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
@@ -77,7 +77,10 @@ public abstract class URIDataStore extends DataStore 
implements StoreResource, R
     private final boolean locationIsPath;
 
     /**
-     * Creates a new data store.
+     * Creates a new data store. This constructor does not open the file,
+     * so subclass constructors can decide whether to open in read-only or 
read/write mode.
+     * It is caller's responsibility to ensure that the {@link 
java.nio.file.OpenOption}
+     * are compatible with whether this data store is read-only or read/write.
      *
      * @param  provider   the factory that created this {@code URIDataStore} 
instance, or {@code null} if unspecified.
      * @param  connector  information about the storage (URL, stream, reader 
instance, <i>etc</i>).
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ascii/Store.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ascii/Store.java
index 411490d10b..0c485df201 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ascii/Store.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ascii/Store.java
@@ -75,7 +75,7 @@ import org.apache.sis.util.resources.Errors;
  * @since   1.2
  * @module
  */
-final class Store extends PRJDataStore implements GridCoverageResource {
+class Store extends PRJDataStore implements GridCoverageResource {
     /**
      * Keys of elements expected in the header. Must be in upper-case letters.
      */
@@ -151,6 +151,9 @@ final class Store extends PRJDataStore implements 
GridCoverageResource {
 
     /**
      * Creates a new ASCII Grid store from the given file, URL or stream.
+     * This constructor opens the file, possibly creating it if the {@code 
connector} contains an
+     * option like {@link java.nio.file.StandardOpenOption#CREATE}, but does 
not try to read it now.
+     * It is possible to open an empty file and have {@link WritableStore} to 
write in it later.
      *
      * @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>).
@@ -449,10 +452,20 @@ cellsize:       if (value != null) {
         return coverage;
     }
 
+    /**
+     * Replaces all data by the given coverage.
+     * This is used for write operations only.
+     */
+    final void setCoverage(final GridCoverage replacement) {
+        gridGeometry = replacement.getGridGeometry();
+        coverage     = replacement;
+        metadata     = null;
+    }
+
     /**
      * Returns the input if it has not been closed.
      */
-    private CharactersView input() throws DataStoreException {
+    final CharactersView input() throws DataStoreException {
         final CharactersView in = input;
         if (in == null) {
             throw new DataStoreClosedException(getLocale(), 
StoreProvider.NAME, StandardOpenOption.READ);
@@ -482,7 +495,7 @@ cellsize:       if (value != null) {
      * Closes this data store after an unrecoverable error occurred.
      * The caller is expected to throw the given exception after this method 
call.
      */
-    private void closeOnError(final Throwable e) {
+    final void closeOnError(final Throwable e) {
         try {
             close();
         } catch (Throwable s) {
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataInput.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataInput.java
index 1a3d63bba1..19d918af63 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataInput.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataInput.java
@@ -106,6 +106,19 @@ public class ChannelDataInput extends ChannelData {
         }
     }
 
+    /**
+     * Returns the length of the stream (in bytes), or -1 if unknown.
+     *
+     * @return the length of the stream (in bytes), or -1 if unknown.
+     * @throws IOException if an error occurred while fetching the stream 
length.
+     */
+    public final long length() throws IOException {     // Method signature 
must match ImageInputStream.length().
+        if (channel instanceof SeekableByteChannel) {
+            return ((SeekableByteChannel) channel).size();
+        }
+        return -1;
+    }
+
     /**
      * Tries to read more bytes from the channel without changing the buffer 
position.
      * This method returns a negative number if the buffer is already full or 
if the channel reached the
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java
index fa63a556dd..679128b80e 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java
@@ -21,7 +21,6 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.SeekableByteChannel;
 import javax.imageio.stream.IIOByteBuffer;
 import javax.imageio.stream.ImageInputStream;
 
@@ -43,7 +42,7 @@ import javax.imageio.stream.ImageInputStream;
  * <p>This class is used when compatibility with {@link 
javax.imageio.ImageReader} is needed.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.5
+ * @version 1.2
  *
  * @see javax.imageio.stream.FileImageInputStream
  * @see javax.imageio.ImageIO#createImageInputStream(Object)
@@ -103,20 +102,6 @@ public class ChannelImageInputStream extends 
ChannelDataInput implements ImageIn
         return buffer.order();
     }
 
-    /**
-     * Returns the length of the stream (in bytes), or -1 if unknown.
-     *
-     * @return the length of the stream (in bytes), or -1 if unknown.
-     * @throws IOException if an error occurred while fetching the stream 
length.
-     */
-    @Override
-    public final long length() throws IOException {
-        if (channel instanceof SeekableByteChannel) {
-            return ((SeekableByteChannel) channel).size();
-        }
-        return -1;
-    }
-
     /**
      * Reads a byte from the stream and returns a {@code true} if it is 
nonzero, {@code false} otherwise.
      * The implementation is as below:
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/ReadOnlyStorageException.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/ReadOnlyStorageException.java
index e6b1c38c1c..f05f9c3e80 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/ReadOnlyStorageException.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/ReadOnlyStorageException.java
@@ -18,7 +18,7 @@ package org.apache.sis.storage;
 
 
 /**
- * Thrown when a {@code DataStore} can not perform a write operations.
+ * Thrown when a {@code DataStore} can not perform a write operation.
  * If a data store does not support any write operation, then it should not 
implement
  * {@link WritableAggregate} or {@link WritableFeatureSet} interface.
  * But in some situations, a data store may implement a {@code Writable*} 
interface
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/ReadOnlyStorageException.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/ResourceAlreadyExistsException.java
similarity index 54%
copy from 
storage/sis-storage/src/main/java/org/apache/sis/storage/ReadOnlyStorageException.java
copy to 
storage/sis-storage/src/main/java/org/apache/sis/storage/ResourceAlreadyExistsException.java
index e6b1c38c1c..4830f66d53 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/ReadOnlyStorageException.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/ResourceAlreadyExistsException.java
@@ -18,31 +18,25 @@ package org.apache.sis.storage;
 
 
 /**
- * Thrown when a {@code DataStore} can not perform a write operations.
- * If a data store does not support any write operation, then it should not 
implement
- * {@link WritableAggregate} or {@link WritableFeatureSet} interface.
- * But in some situations, a data store may implement a {@code Writable*} 
interface
- * and nevertheless be unable to perform a write operation, for example 
because the
- * underlying {@link java.nio.channels.Channel} is read-only or part of the 
file is
- * locked by another process.
+ * Thrown when a write operation can not be performed because it would cause 
the replacement of an existing resource.
+ * Replacements can be performed only if an option such as {@link 
WritableGridCoverageResource.CommonOption#REPLACE}
+ * has been specified.
  *
- * @author  Johann Sorel (Geomatys)
- * @version 0.8
- * @since   0.8
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since   1.2
  * @module
- *
- * @see ForwardOnlyStorageException
  */
-public class ReadOnlyStorageException extends DataStoreException {
+public class ResourceAlreadyExistsException extends DataStoreException {
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = 5710116172772560023L;
+    private static final long serialVersionUID = 3854120553618596031L;
 
     /**
      * Creates an exception with no cause and no details message.
      */
-    public ReadOnlyStorageException() {
+    public ResourceAlreadyExistsException() {
     }
 
     /**
@@ -50,26 +44,17 @@ public class ReadOnlyStorageException extends 
DataStoreException {
      *
      * @param message  the detail message.
      */
-    public ReadOnlyStorageException(final String message) {
+    public ResourceAlreadyExistsException(String message) {
         super(message);
     }
 
-    /**
-     * Creates an exception with the specified cause and no details message.
-     *
-     * @param cause  the cause for this exception.
-     */
-    public ReadOnlyStorageException(final Throwable cause) {
-        super(cause);
-    }
-
     /**
      * Creates an exception with the specified details message and cause.
      *
      * @param message  the detail message.
      * @param cause    the cause for this exception.
      */
-    public ReadOnlyStorageException(final String message, final Throwable 
cause) {
+    public ResourceAlreadyExistsException(String message, Throwable cause) {
         super(message, cause);
     }
 }
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/WritableGridCoverageResource.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/WritableGridCoverageResource.java
index 50248ad92c..254884b6e3 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/WritableGridCoverageResource.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/WritableGridCoverageResource.java
@@ -35,7 +35,7 @@ public interface WritableGridCoverageResource extends 
GridCoverageResource {
      * Configuration of the process of writing a coverage in a data store.
      * By default, the {@linkplain #write write operation} is conservative: no 
operation is executed
      * if it would result in data lost. {@code Option} allows to modify this 
behavior for example by
-     * allowing the {@linkplain CommonOption#TRUNCATE replacement} of previous 
data.
+     * allowing the {@link CommonOption#REPLACE replacement} of previous data.
      * Options can also configure other aspects like compression, version or 
encryption.
      *
      * <p>Some options may be {@link DataStore}-specific.
@@ -71,21 +71,29 @@ public interface WritableGridCoverageResource extends 
GridCoverageResource {
          *   <li>If there are no existing coverages in the {@link 
GridCoverageResource},
          *       then the new coverage will be inserted as if this option was 
not provided.</li>
          * </ul>
+         *
+         * This option is mutually exclusive with {@link #UPDATE}.
          */
-        TRUNCATE,
+        REPLACE,
 
         /**
          * Updates or appends existing coverage with new data.
-         * If a coverage already exists when the {@linkplain #write write 
operation} is executed with this option, then:
+         * If this option is specified, then there is a choice:
          *
          * <ul>
-         *   <li>Areas of the provided {@link GridCoverage} that are within 
the existing {@link GridGeometry}
-         *       will overwrite the existing data.</li>
-         *   <li>Areas outside the existing {@link GridGeometry} will result 
in expanding the grid geometry
-         *       with the new data.</li>
+         *   <li>If a coverage already exists in the {@link 
GridCoverageResource}, then:
+         *     <ul>
+         *       <li>Areas of the provided {@link GridCoverage} that are 
within the existing {@link GridGeometry}
+         *           will overwrite the existing data.</li>
+         *       <li>Areas outside the existing {@link GridGeometry} will 
result in expanding the grid geometry
+         *           with the new data.</li>
+         *     </ul>
+         *   </li>
+         *   <li>If there are no existing coverages in the {@link 
GridCoverageResource},
+         *       then the new coverage is inserted as if this option was not 
provided.</li>
          * </ul>
          *
-         * If there are no previous coverages, then the new coverage is 
inserted as if this option was not provided.
+         * This option is mutually exclusive with {@link #REPLACE}.
          */
         UPDATE
     }
@@ -94,13 +102,19 @@ public interface WritableGridCoverageResource extends 
GridCoverageResource {
      * Writes a new coverage in the data store for this resource. If a 
coverage already exists for this resource,
      * then the behavior of this method is determined by the given options. If 
no option is specified, the default
      * behavior is to fail if writing a coverage would cause an existing 
coverage to be overwritten.
-     * This behavior can be modified by requesting the {@linkplain 
CommonOption#TRUNCATE replacement}
+     * This behavior can be modified by requesting the {@link 
CommonOption#REPLACE replacement}
      * or {@linkplain CommonOption#UPDATE update} of existing coverages.
      *
      * @param  coverage  new data to write in the data store for this resource.
      * @param  options   configuration of the write operation. May be {@link 
DataStore}-specific options
      *                   (e.g. for compression, encryption, <i>etc</i>).
-     * @throws DataStoreException if an error occurred while writing data in 
the underlying data store.
+     * @throws IllegalArgumentException if mutually exclusive options are 
specified.
+     * @throws ReadOnlyStorageException if the resource is (possibly 
temporarily) read-only.
+     * @throws ResourceAlreadyExistsException if a coverage already exists in 
this resource
+     *         and no {@code REPLACE} or {@code UPDATE} option have been 
specified.
+     * @throws IncompatibleResourceException if the given resource can not be 
written,
+     *         for example because its grid geometry is unsupported by this 
resource.
+     * @throws DataStoreException if another error occurred while writing data 
in the underlying data store.
      */
     void write(GridCoverage coverage, Option... options) throws 
DataStoreException;
 }

Reply via email to