You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2023/10/07 20:23:41 UTC
[sis] 02/06: `ChannelDataInput` implements `DataInput` for allowing `instanceof` checks against a public interface.
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 255a5b2bd73bcf4873d78a677eed30140f843e6d
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sun Sep 17 14:51:39 2023 +0200
`ChannelDataInput` implements `DataInput` for allowing `instanceof` checks against a public interface.
---
.../org/apache/sis/io/stream/ChannelDataInput.java | 125 +++++++++++++++++++--
.../sis/io/stream/ChannelImageInputStream.java | 70 +-----------
.../apache/sis/io/stream/InputStreamAdapter.java | 58 +++++++++-
.../org/apache/sis/storage/StorageConnector.java | 99 ++++++++--------
.../apache/sis/storage/StorageConnectorTest.java | 10 +-
5 files changed, 227 insertions(+), 135 deletions(-)
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelDataInput.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelDataInput.java
index 5f0c4c5c9e..a7aa272888 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelDataInput.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelDataInput.java
@@ -16,10 +16,13 @@
*/
package org.apache.sis.io.stream;
+import java.io.DataInput;
+import java.io.DataInputStream;
import java.io.IOException;
import java.io.EOFException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.ShortBuffer;
import java.nio.IntBuffer;
@@ -51,17 +54,14 @@ import static org.apache.sis.util.ArgumentChecks.ensureBetween;
* <p>Since this class is only a helper tool, it does not "own" the channel and consequently does not provide
* {@code close()} method. It is users responsibility to close the channel after usage.</p>
*
- * <h2>Relationship with {@code DataInput}</h2>
- * This class API is compatibly with the {@link java.io.DataInput} interface, so subclasses can implement that
- * interface if they wish. This class does not implement {@code DataInput} itself because it is not needed for
- * SIS purposes, and because {@code DataInput} has undesirable methods ({@code readLine()} and {@code readUTF()}).
- * However, the {@link ChannelImageInputStream} class implements the {@code DataInput} interface, together with
- * the {@link javax.imageio.stream.ImageInputStream} one, mostly for situations when inter-operability with
- * {@link javax.imageio} is needed.
+ * <h2>Relationship with {@code ChannelImageInputStream}</h2>
+ * This class API is compatible with the {@link javax.imageio.stream.ImageInputStream} interface, so subclasses
+ * can implement that interface if they wish. This is done by {@link ChannelImageInputStream} for situations
+ * when inter-operability with {@link javax.imageio} is needed.
*
* @author Martin Desruisseaux (Geomatys)
*/
-public class ChannelDataInput extends ChannelData {
+public class ChannelDataInput extends ChannelData implements DataInput {
/**
* Minimum number of bytes to skip in the {@code seek(long)} operation.
* If there is less bytes to skip, then it is not worth to do a seek
@@ -173,7 +173,7 @@ public class ChannelDataInput extends ChannelData {
/**
* Returns {@code true} if the buffer or the channel has at least one byte remaining.
- * If the {@linkplain #buffer buffer} has no remaining bytes, then this method will attempts
+ * If the {@linkplain #buffer buffer} has no remaining bytes, then this method will attempt
* to read at least one byte from the {@linkplain #channel}. If no bytes can be read because
* the channel has reached the end of stream, then this method returns {@code false}.
*
@@ -244,10 +244,10 @@ public class ChannelDataInput extends ChannelData {
}
/**
- * Pushes back the last processed byte. This is used when a call to {@code readBit()} did not
- * used every bits in a byte, or when {@code readLine()} checked for the Windows-style of EOL.
+ * Pushes back the last processed byte. This is used when a call to {@link #readBits(int)} did not
+ * used every bits in a byte, or when {@link #readLine()} checked for the Windows-style of EOL.
*/
- final void pushBack() {
+ private void pushBack() {
buffer.position(buffer.position() - 1);
}
@@ -257,6 +257,8 @@ public class ChannelDataInput extends ChannelData {
*
* @return the value of the next bit from the stream.
* @throws IOException if an error occurred while reading (including EOF).
+ *
+ * @see #readBoolean()
*/
public final int readBit() throws IOException {
ensureBufferContains(Byte.BYTES);
@@ -297,6 +299,26 @@ public class ChannelDataInput extends ChannelData {
return value;
}
+ /**
+ * Reads a byte from the stream and returns {@code true} if it is nonzero, {@code false} otherwise.
+ * The implementation is as below:
+ *
+ * {@snippet lang="java" :
+ * return readByte() != 0;
+ * }
+ *
+ * For reading a single bit, use {@link #readBit()} instead.
+ *
+ * @return the value of the next boolean from the stream.
+ * @throws IOException if an error (including EOF) occurred while reading the stream.
+ *
+ * @see #readBit()
+ */
+ @Override
+ public final boolean readBoolean() throws IOException {
+ return readByte() != 0;
+ }
+
/**
* Reads the next byte value (8 bits) from the stream. This method ensures that there is at
* least 1 byte remaining in the buffer, reading new bytes from the channel if necessary,
@@ -305,6 +327,7 @@ public class ChannelDataInput extends ChannelData {
* @return the value of the next byte from the stream.
* @throws IOException if an error (including EOF) occurred while reading the stream.
*/
+ @Override
public final byte readByte() throws IOException {
ensureBufferContains(Byte.BYTES);
return buffer.get();
@@ -321,6 +344,7 @@ public class ChannelDataInput extends ChannelData {
* @return the value of the next unsigned byte from the stream.
* @throws IOException if an error (including EOF) occurred while reading the stream.
*/
+ @Override
public final int readUnsignedByte() throws IOException {
return Byte.toUnsignedInt(readByte());
}
@@ -333,6 +357,7 @@ public class ChannelDataInput extends ChannelData {
* @return the value of the next short from the stream.
* @throws IOException if an error (including EOF) occurred while reading the stream.
*/
+ @Override
public final short readShort() throws IOException {
ensureBufferContains(Short.BYTES);
return buffer.getShort();
@@ -349,6 +374,7 @@ public class ChannelDataInput extends ChannelData {
* @return the value of the next unsigned short from the stream.
* @throws IOException if an error (including EOF) occurred while reading the stream.
*/
+ @Override
public final int readUnsignedShort() throws IOException {
return Short.toUnsignedInt(readShort());
}
@@ -361,6 +387,7 @@ public class ChannelDataInput extends ChannelData {
* @return the value of the next character from the stream.
* @throws IOException if an error (including EOF) occurred while reading the stream.
*/
+ @Override
public final char readChar() throws IOException {
ensureBufferContains(Character.BYTES);
return buffer.getChar();
@@ -374,6 +401,7 @@ public class ChannelDataInput extends ChannelData {
* @return the value of the next integer from the stream.
* @throws IOException if an error (including EOF) occurred while reading the stream.
*/
+ @Override
public final int readInt() throws IOException {
ensureBufferContains(Integer.BYTES);
return buffer.getInt();
@@ -402,6 +430,7 @@ public class ChannelDataInput extends ChannelData {
* @return the value of the next integer from the stream.
* @throws IOException if an error (including EOF) occurred while reading the stream.
*/
+ @Override
public final long readLong() throws IOException {
ensureBufferContains(Long.BYTES);
return buffer.getLong();
@@ -415,6 +444,7 @@ public class ChannelDataInput extends ChannelData {
* @return the value of the next float from the stream.
* @throws IOException if an error (including EOF) occurred while reading the stream.
*/
+ @Override
public final float readFloat() throws IOException {
ensureBufferContains(Float.BYTES);
return buffer.getFloat();
@@ -428,6 +458,7 @@ public class ChannelDataInput extends ChannelData {
* @return the value of the next double from the stream.
* @throws IOException if an error (including EOF) occurred while reading the stream.
*/
+ @Override
public final double readDouble() throws IOException {
ensureBufferContains(Double.BYTES);
return buffer.getDouble();
@@ -542,6 +573,7 @@ public class ChannelDataInput extends ChannelData {
* @param dest An array of bytes to be written to.
* @throws IOException if an error (including EOF) occurred while reading the stream.
*/
+ @Override
public final void readFully(final byte[] dest) throws IOException {
readFully(dest, 0, dest.length);
}
@@ -555,6 +587,7 @@ public class ChannelDataInput extends ChannelData {
* @param length the number of bytes to read.
* @throws IOException if an error (including EOF) occurred while reading the stream.
*/
+ @Override
public final void readFully(final byte[] dest, int offset, int length) throws IOException {
while (length != 0) {
ensureNonEmpty();
@@ -896,6 +929,74 @@ public class ChannelDataInput extends ChannelData {
return new String(array, position, length, encoding);
}
+ /**
+ * Reads in a string that has been encoded using a UTF-8 string.
+ *
+ * @return the string reads from the stream.
+ * @throws IOException if an error (including EOF) occurred while reading the stream.
+ */
+ @Override
+ public final String readUTF() throws IOException {
+ final ByteOrder oldOrder = buffer.order();
+ buffer.order(ByteOrder.BIG_ENDIAN);
+ try {
+ return DataInputStream.readUTF(this);
+ } finally {
+ buffer.order(oldOrder);
+ }
+ }
+
+ /**
+ * Reads new bytes until the next EOL. This method can read only US-ASCII strings.
+ * This method is provided for compliance with the {@link DataInput} interface,
+ * but is generally not recommended.
+ *
+ * @return the next line, or {@code null} if the EOF has been reached.
+ * @throws IOException if an error occurred while reading.
+ */
+ @Override
+ public final String readLine() throws IOException {
+ if (!hasRemaining()) {
+ return null;
+ }
+ int c = Byte.toUnsignedInt(buffer.get());
+ StringBuilder line = new StringBuilder();
+ line.append((char) c);
+loop: while (hasRemaining()) {
+ c = Byte.toUnsignedInt(buffer.get());
+ switch (c) {
+ case '\n': break loop;
+ case '\r': {
+ if (hasRemaining() && buffer.get() != '\n') {
+ pushBack();
+ }
+ break loop;
+ }
+ }
+ line.append((char) c);
+ }
+ return line.toString();
+ }
+
+ /**
+ * Tries to skip over <var>n</var> bytes of data from the input stream.
+ * This method may skip over some smaller number of bytes, possibly zero.
+ * A negative value move backward in the input stream.
+ *
+ * @param n maximal number of bytes to skip. Can be negative.
+ * @return number of bytes actually skipped.
+ * @throws IOException if an error occurred while reading.
+ */
+ @Override
+ public int skipBytes(int n) throws IOException {
+ if (!hasRemaining()) {
+ return 0;
+ }
+ n = Math.min(n, buffer.remaining());
+ buffer.position(buffer.position() + n);
+ return n;
+ }
+
/**
* Moves to the given position in the stream. The given position is relative to
* the position that the stream had at {@code ChannelDataInput} construction time.
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelImageInputStream.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelImageInputStream.java
index e39b7cf8ae..04eeaead32 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelImageInputStream.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelImageInputStream.java
@@ -16,7 +16,6 @@
*/
package org.apache.sis.io.stream;
-import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -110,73 +109,6 @@ public class ChannelImageInputStream extends ChannelDataInput implements ImageIn
return buffer.order();
}
- /**
- * Reads a byte from the stream and returns a {@code true} if it is nonzero, {@code false} otherwise.
- * The implementation is as below:
- *
- * {@snippet lang="java" :
- * return readByte() != 0;
- * }
- *
- * @return the value of the next boolean from the stream.
- * @throws IOException if an error (including EOF) occurred while reading the stream.
- */
- @Override
- public final boolean readBoolean() throws IOException {
- return readByte() != 0;
- }
-
- /**
- * Reads in a string that has been encoded using a UTF-8 string.
- *
- * @return the string reads from the stream.
- * @throws IOException if an error (including EOF) occurred while reading the stream.
- */
- @Override
- public final String readUTF() throws IOException {
- final ByteOrder oldOrder = buffer.order();
- buffer.order(ByteOrder.BIG_ENDIAN);
- try {
- return DataInputStream.readUTF(this);
- } finally {
- buffer.order(oldOrder);
- }
- }
-
- /**
- * Reads the new bytes until the next EOL. This method can read only US-ASCII strings.
- * This method is provided for compliance with the {@link java.io.DataInput} interface,
- * but is generally not recommended.
- *
- * @return the next line, or {@code null} if the EOF has been reached.
- * @throws IOException if an error occurred while reading.
- */
- @Override
- public final String readLine() throws IOException {
- int c = read();
- if (c < 0) {
- return null;
- }
- StringBuilder line = new StringBuilder();
- line.append((char) c);
-loop: while ((c = read()) >= 0) {
- switch (c) {
- case '\r': {
- c = read();
- if (c >= 0 && c != '\n') {
- pushBack();
- }
- break loop;
- }
- case '\n': {
- break loop;
- }
- }
- line.append((char) c);
- }
- return line.toString();
- }
-
/**
* Returns the next byte from the stream as an unsigned integer between 0 and 255,
* or -1 if we reached the end of stream.
@@ -259,7 +191,7 @@ loop: while ((c = read()) >= 0) {
* But experience shows that various {@code ImageReader} implementations outside Apache SIS
* expect that we skip exactly the specified amount of bytes and ignore the returned value.
*
- * @param n maximal number of bytes to skip. Can be negative.
+ * @param n number of bytes to skip. Can be negative.
* @return number of bytes actually skipped.
* @throws IOException if an error occurred while reading.
*/
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/InputStreamAdapter.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/InputStreamAdapter.java
index 92907a8c85..81f05db4b8 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/InputStreamAdapter.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/InputStreamAdapter.java
@@ -42,8 +42,8 @@ import org.apache.sis.storage.internal.Resources;
public final class InputStreamAdapter extends InputStream implements Markable {
/**
* The underlying data input stream. In principle, public access to this field breaks encapsulation.
- * But since {@code InputStreamAdapter} does not hold any state and just forwards every method calls
- * to that {@code ImageInputStream}, using on object or the other does not make a difference.
+ * But since {@code InputStreamAdapter} forwards most method calls to that {@code ImageInputStream},
+ * using an object or the other does not make a difference except for marks and close operations.
*/
public final ImageInputStream input;
@@ -119,6 +119,60 @@ public final class InputStreamAdapter extends InputStream implements Markable {
return input.read(b, off, len);
}
+ /**
+ * Reads up to a specified number of bytes from the input stream.
+ * This method may read less bytes if the end-of-stream is reached.
+ *
+ * @param count number of bytes to read.
+ * @return the bytes read.
+ * @throws IOException if an I/O error occurs.
+ */
+ @Override
+ public byte[] readNBytes(int count) throws IOException {
+ final long length = input.length();
+ if (length < 0) {
+ return super.readNBytes(count);
+ }
+ count = (int) Math.min(count, Math.subtractExact(length, input.getStreamPosition()));
+ final byte[] array = new byte[count];
+ input.readFully(array);
+ return array;
+ }
+
+ /**
+ * Reads up to a specified number of bytes from the input stream.
+ * This method may read less bytes if the end-of-stream is reached.
+ *
+ * @param array where to store the bytes.
+ * @param offset index if the first element where to store bytes.
+ * @param count number of bytes to read.
+ * @return number of bytes actually read.
+ * @throws IOException if an I/O error occurs.
+ */
+ @Override
+ public int readNBytes(final byte[] array, final int offset, int count) throws IOException {
+ final long length = input.length();
+ if (length < 0) {
+ return super.readNBytes(array, offset, count);
+ }
+ count = (int) Math.min(count, Math.subtractExact(length, input.getStreamPosition()));
+ input.readFully(array, offset, count);
+ return count;
+ }
+
+ /**
+ * Skips the specified number of bytes. If the final position is past the end of file,
+ * an {@link java.io.EOFException} will be thrown either by this method or at the next
+ * read operation.
+ *
+ * @param count number of bytes to skip.
+ * @throws IOException if an I/O error occurs.
+ */
+// @Override // Pending JDK12.
+ public void skipNBytes(final long count) throws IOException {
+ input.seek(Math.addExact(input.getStreamPosition(), count));
+ }
+
/**
* Skips over and discards {@code n} bytes of data from this input stream.
*
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/StorageConnector.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/StorageConnector.java
index 86ee0df6bf..0be3ca9ad4 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/StorageConnector.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/StorageConnector.java
@@ -213,7 +213,7 @@ public class StorageConnector implements Serializable {
add(OutputStream.class, StorageConnector::createOutputStream);
add(Reader.class, StorageConnector::createReader);
add(Connection.class, StorageConnector::createConnection);
- add(ChannelDataInput.class, (s) -> s.createChannelDataInput(false)); // Undocumented case (SIS internal)
+ add(ChannelDataInput.class, StorageConnector::createChannelDataInput); // Undocumented case (SIS internal)
add(ChannelDataOutput.class, StorageConnector::createChannelDataOutput); // Undocumented case (SIS internal)
add(ChannelFactory.class, (s) -> null); // Undocumented. Shall not cache.
/*
@@ -1017,13 +1017,12 @@ public class StorageConnector implements Serializable {
* This method is one of the {@link #OPENERS} methods and should be invoked at most once per
* {@code StorageConnector} instance.
*
- * @param asImageInputStream whether the {@code ChannelDataInput} needs to be {@link ChannelImageInputStream} subclass.
* @return input channel, or {@code null} if none or if {@linkplain #probing} result has been determined offline.
* @throws IOException if an error occurred while opening a channel for the input.
*
* @see #createChannelDataOutput()
*/
- private ChannelDataInput createChannelDataInput(final boolean asImageInputStream) throws IOException, DataStoreException {
+ private ChannelDataInput createChannelDataInput() throws IOException, DataStoreException {
/*
* Before to try to wrap an InputStream, mark its position so we can rewind if the user asks for
* the InputStream directly. We need to reset because ChannelDataInput may have read some bytes.
@@ -1065,12 +1064,7 @@ public class StorageConnector implements Serializable {
final ReadableByteChannel channel = factory.readable(name, null);
addView(ReadableByteChannel.class, channel, null, factory.isCoupled() ? CASCADE_ON_RESET : 0);
final ByteBuffer buffer = getChannelBuffer(factory);
- final ChannelDataInput asDataInput;
- if (asImageInputStream) {
- asDataInput = new ChannelImageInputStream(name, channel, buffer, false);
- } else {
- asDataInput = new ChannelDataInput(name, channel, buffer, false);
- }
+ final ChannelDataInput asDataInput = new ChannelDataInput(name, channel, buffer, false);
addView(ChannelDataInput.class, asDataInput, ReadableByteChannel.class, CASCADE_ON_RESET);
/*
* Following is an undocumented mechanism for allowing some Apache SIS implementations of DataStore
@@ -1107,20 +1101,15 @@ public class StorageConnector implements Serializable {
if (reset(c)) {
in = (ChannelDataInput) c.view;
} else {
- in = createChannelDataInput(true); // May be null.
+ in = createChannelDataInput(); // May be null. This method should not have been invoked before.
}
final DataInput asDataInput;
if (in != null) {
- c = getView(ChannelDataInput.class); // May have been added by createChannelDataInput(…).
- if (in instanceof DataInput) {
- asDataInput = (DataInput) in;
- } else {
- asDataInput = new ChannelImageInputStream(in); // Upgrade existing instance.
- c.view = asDataInput;
- }
- views.put(DataInput.class, c); // Share the same Coupled instance.
+ asDataInput = in;
+ c = getView(ChannelDataInput.class); // Refresh because may have been added by createChannelDataInput().
+ views.put(DataInput.class, c); // Share the same `Coupled` instance.
} else if (wasProbingAbsentFile()) {
- return null; // Do not cache, for allowing file creation later.
+ return null; // Do not cache, for allowing file creation later.
} else {
reset();
try {
@@ -1195,8 +1184,8 @@ public class StorageConnector implements Serializable {
/*
* If no ChannelDataInput has been created by the above code, get the input as an ImageInputStream and
* read an arbitrary number of bytes. Read only a small amount of bytes because, at the contrary of the
- * buffer created in `createChannelDataInput(boolean)`, the buffer created here is unlikely to be used
- * for the reading process after the recognition of the file format.
+ * buffer created in `createChannelDataInput()`, the buffer created here is unlikely to be used for the
+ * reading process after the recognition of the file format.
*/
final ImageInputStream in = getStorageAs(ImageInputStream.class);
if (in != null) {
@@ -1271,31 +1260,46 @@ public class StorageConnector implements Serializable {
}
/**
- * Creates an {@link ImageInputStream} from the {@link DataInput} if possible. This method simply
- * casts {@code DataInput} if such cast is allowed. Since {@link #createDataInput()} instantiates
- * {@link ChannelImageInputStream}, this cast is usually possible.
+ * Creates an {@link ImageInputStream} from the {@link DataInput} if possible. This method casts
+ * {@code DataInput} if such cast is allowed, or upgrades {@link ChannelDataInput} implementation.
*
* <p>This method is one of the {@link #OPENERS} methods and should be invoked at most once per
* {@code StorageConnector} instance.</p>
*
* @return input stream, or {@code null} if none or if {@linkplain #probing} result has been determined offline.
*/
- private ImageInputStream createImageInputStream() throws DataStoreException {
- final Class<DataInput> source = DataInput.class;
- final DataInput input = getStorageAs(source);
+ private ImageInputStream createImageInputStream() throws IOException, DataStoreException {
+ final Coupled c;
+ final ImageInputStream asDataInput;
+ DataInput input = getStorageAs(DataInput.class);
if (input instanceof ImageInputStream) {
- views.put(ImageInputStream.class, views.get(source)); // Share the same Coupled instance.
- return (ImageInputStream) input;
- } else if (!wasProbingAbsentFile()) {
- /*
- * We do not invoke `ImageIO.createImageInputStream(Object)` because we do not know
- * how the stream will use the `storage` object. It may read in advance some bytes,
- * which can invalidate the storage for use outside the `ImageInputStream`. Instead
- * creating image input/output streams is left to caller's responsibility.
- */
- addView(ImageInputStream.class, null); // Remember that there is no view.
+ asDataInput = (ImageInputStream) input;
+ c = views.get(DataInput.class);
+ } else {
+ input = getStorageAs(ChannelDataInput.class);
+ if (input != null) {
+ c = getView(ChannelDataInput.class);
+ if (input instanceof ImageInputStream) {
+ asDataInput = (ImageInputStream) input;
+ } else {
+ asDataInput = new ChannelImageInputStream((ChannelDataInput) input);
+ c.view = asDataInput; // Upgrade existing instance for all views.
+ }
+ } else {
+ if (!wasProbingAbsentFile()) {
+ /*
+ * We do not invoke `ImageIO.createImageInputStream(Object)` because we do not know
+ * how the stream will use the `storage` object. It may read in advance some bytes,
+ * which can invalidate the storage for use outside the `ImageInputStream`. Instead
+ * creating image input/output streams is left to caller's responsibility.
+ */
+ addView(ImageInputStream.class, null); // Remember that there is no view.
+ }
+ return null;
+ }
}
- return null;
+ views.put(ImageInputStream.class, c); // Share the same `Coupled` instance.
+ return asDataInput;
}
/**
@@ -1310,18 +1314,19 @@ public class StorageConnector implements Serializable {
* @see #createOutputStream()
*/
private InputStream createInputStream() throws IOException, DataStoreException {
- final Class<DataInput> source = DataInput.class;
- final DataInput input = getStorageAs(source);
- if (input instanceof InputStream) {
- views.put(InputStream.class, views.get(source)); // Share the same Coupled instance.
- return (InputStream) input;
- } else if (input instanceof ImageInputStream) {
+ final Class<ImageInputStream> source = ImageInputStream.class;
+ final ImageInputStream input = getStorageAs(source);
+ if (input != null) {
+ if (input instanceof InputStream) {
+ views.put(InputStream.class, views.get(source)); // Share the same `Coupled` instance.
+ return (InputStream) input;
+ }
/*
* Wrap the ImageInputStream as an ordinary InputStream. We avoid setting CASCADE_ON_RESET (unless
* reset() needs to propagate further than ImageInputStream) because changes in InputStreamAdapter
* position are immediately reflected by corresponding changes in ImageInputStream position.
*/
- final InputStream in = new InputStreamAdapter((ImageInputStream) input);
+ final InputStream in = new InputStreamAdapter(input);
addView(InputStream.class, in, source, (byte) (getView(source).cascade & CASCADE_ON_RESET));
return in;
} else if (!wasProbingAbsentFile()) {
@@ -1418,7 +1423,7 @@ public class StorageConnector implements Serializable {
* @return output channel, or {@code null} if none.
* @throws IOException if an error occurred while opening a channel for the output.
*
- * @see #createChannelDataInput(boolean)
+ * @see #createChannelDataInput()
*/
private ChannelDataOutput createChannelDataOutput() throws IOException, DataStoreException {
/*
@@ -1517,7 +1522,7 @@ public class StorageConnector implements Serializable {
final Class<DataOutput> target = DataOutput.class;
final DataOutput output = getStorageAs(target);
if (output instanceof OutputStream) {
- views.put(OutputStream.class, views.get(target)); // Share the same Coupled instance.
+ views.put(OutputStream.class, views.get(target)); // Share the same `Coupled` instance.
return (OutputStream) output;
} else {
addView(OutputStream.class, null); // Remember that there is no view.
diff --git a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/StorageConnectorTest.java b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/StorageConnectorTest.java
index 2f010e4a61..bed05efd43 100644
--- a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/StorageConnectorTest.java
+++ b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/StorageConnectorTest.java
@@ -201,13 +201,13 @@ public final class StorageConnectorTest extends TestCase {
assertEquals(asStream, connector.getStorageAs(Path.class) == null);
final DataInput input = connector.getStorageAs(DataInput.class);
assertSame("Value shall be cached.", input, connector.getStorageAs(DataInput.class));
- assertInstanceOf("Needs the SIS implementation.", ChannelImageInputStream.class, input);
+ assertInstanceOf("Needs the SIS implementation.", ChannelDataInput.class, input);
assertSame("Instance shall be shared.", input, connector.getStorageAs(ChannelDataInput.class));
/*
* Reads a single integer for checking that the stream is at the right position, then close the stream.
* Since the file is a compiled Java class, the integer that we read shall be the Java magic number.
*/
- final ReadableByteChannel channel = ((ChannelImageInputStream) input).channel;
+ final ReadableByteChannel channel = ((ChannelDataInput) input).channel;
assertTrue("channel.isOpen()", channel.isOpen());
assertEquals("First 4 bytes", MAGIC_NUMBER, input.readInt());
connector.closeAllExcept(null);
@@ -343,7 +343,7 @@ public final class StorageConnectorTest extends TestCase {
/*
* Get as an image input stream and ensure that the cached value has been replaced.
*/
- final DataInput stream = connector.getStorageAs(DataInput.class);
+ final ImageInputStream stream = connector.getStorageAs(ImageInputStream.class);
assertInstanceOf("Needs the SIS implementation", ChannelImageInputStream.class, stream);
assertNotSame("Expected a new instance.", input, stream);
assertSame("Shall share the channel.", input.channel, ((ChannelDataInput) stream).channel);
@@ -439,8 +439,8 @@ public final class StorageConnectorTest extends TestCase {
@DependsOnMethod("testGetAsDataInputFromStream")
public void testCloseAllExcept() throws DataStoreException, IOException {
final StorageConnector connector = create(true);
- final DataInput input = connector.getStorageAs(DataInput.class);
- final ReadableByteChannel channel = ((ChannelImageInputStream) input).channel;
+ final ChannelDataInput input = connector.getStorageAs(ChannelDataInput.class);
+ final ReadableByteChannel channel = input.channel;
assertTrue("channel.isOpen()", channel.isOpen());
connector.closeAllExcept(input);
assertTrue("channel.isOpen()", channel.isOpen());