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 2013/06/04 15:18:35 UTC
svn commit: r1489435 - in /sis/branches/JDK7/storage:
sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/
sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/
sis-storage/src/main/java/org/apache/sis/internal/storage/ sis-storage/src...
Author: desruisseaux
Date: Tue Jun 4 13:18:34 2013
New Revision: 1489435
URL: http://svn.apache.org/r1489435
Log:
ChannelDecoder constructor now expect an explicit ChannelDataInput,
and implementation delegates the String decoding to ChannelDataInput.
Modified:
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreConnection.java
sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java
sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreConnectionTest.java
Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java?rev=1489435&r1=1489434&r2=1489435&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java [UTF-8] Tue Jun 4 13:18:34 2013
@@ -174,25 +174,22 @@ public final class ChannelDecoder extend
* This constructor parses immediately the header.
*
* @param sink Where to send the warnings, or {@code null} if none.
- * @param filename A file identifier used only for formatting error message.
- * @param channel The channel from where data are read.
+ * @param input The channel and the buffer from where data are read.
* @throws IOException If an error occurred while reading the channel.
* @throws DataStoreException If the content of the given channel is not a NetCDF file.
*/
- public ChannelDecoder(final WarningProducer sink, final String filename, final ReadableByteChannel channel)
+ public ChannelDecoder(final WarningProducer sink, final ChannelDataInput input)
throws IOException, DataStoreException
{
super(sink);
- // The buffer must be backed by a Java {@code byte[]} array,
- // because we will occasionally reference that array.
- input = new ChannelDataInput(filename, channel, ByteBuffer.allocate(4096), false);
+ this.input = input;
/*
* Check the magic number, which is expected to be exactly 3 bytes forming the "CDF" string.
* The 4th byte is the version number, which we opportunistically use after the magic number check.
*/
int version = input.readInt();
if ((version & 0xFFFFFF00) != (('C' << 24) | ('D' << 16) | ('F' << 8))) {
- throw new DataStoreException(errors().getString(Errors.Keys.UnexpectedFileFormat_2, "NetCDF", filename));
+ throw new DataStoreException(errors().getString(Errors.Keys.UnexpectedFileFormat_2, "NetCDF", input.filename));
}
/*
* Check the version number.
@@ -311,9 +308,8 @@ public final class ChannelDecoder extend
}
final ByteBuffer buffer = input.buffer;
final int size = ensureBufferContains(length, 1, "<name>");
- final int position = buffer.position(); // Must be after 'require'
- final String text = new String(buffer.array(), position, length, NAME_ENCODING);
- buffer.position(position + size);
+ final String text = input.readString(length, NAME_ENCODING);
+ buffer.position(buffer.position() + (size - length));
return text;
}
@@ -330,11 +326,11 @@ public final class ChannelDecoder extend
}
final ByteBuffer buffer = input.buffer;
final int size = ensureBufferContains(length, VariableInfo.sizeOf(type), name);
- final int position = buffer.position(); // Must be after 'require'
+ final int position = buffer.position(); // Must be after 'ensureBufferContains'
final Object result;
switch (type) {
case VariableInfo.CHAR: {
- final String text = new String(buffer.array(), position, length, encoding).trim();
+ final String text = input.readString(length, encoding).trim();
result = text.isEmpty() ? null : text;
break;
}
Modified: sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java?rev=1489435&r1=1489434&r2=1489435&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java [UTF-8] Tue Jun 4 13:18:34 2013
@@ -18,10 +18,12 @@ package org.apache.sis.internal.netcdf.i
import java.io.IOException;
import java.io.InputStream;
+import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import org.opengis.wrapper.netcdf.IOTestCase;
import org.apache.sis.internal.netcdf.Decoder;
import org.apache.sis.internal.netcdf.DecoderTest;
+import org.apache.sis.internal.storage.ChannelDataInput;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.test.DependsOn;
@@ -66,8 +68,10 @@ public final strictfp class ChannelDecod
public static Decoder createChannelDecoder(final String name) throws IOException {
final InputStream in = IOTestCase.class.getResourceAsStream(name);
assertNotNull(name, in);
+ final ChannelDataInput input = new ChannelDataInput(name,
+ Channels.newChannel(in), ByteBuffer.allocate(4096), false);
try {
- return new ChannelDecoder(null, name, Channels.newChannel(in));
+ return new ChannelDecoder(null, input);
} catch (DataStoreException e) {
throw new AssertionError(e);
}
Modified: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java?rev=1489435&r1=1489434&r2=1489435&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java [UTF-8] Tue Jun 4 13:18:34 2013
@@ -182,7 +182,7 @@ public class ChannelDataInput {
* @throws EOFException If the channel has reached the end of stream.
* @throws IOException If an other kind of error occurred while reading.
*/
- public final void ensureBufferContains(int n) throws IOException {
+ public final void ensureBufferContains(int n) throws EOFException, IOException {
assert n <= buffer.capacity() : n;
n -= buffer.remaining();
if (n > 0) {
@@ -644,6 +644,30 @@ public class ChannelDataInput {
}
/**
+ * Decodes a string from a sequence of bytes in the given encoding. This method tries to avoid the creation
+ * of a temporary {@code byte[]} array when possible.
+ *
+ * <p>This convenience method shall be used only for relatively small amount of {@link String} instances
+ * to decode, for example attribute values in the file header. For large amount of data, consider using
+ * {@link java.nio.charset.CharsetDecoder} instead.</p>
+ *
+ * @param length Number of bytes to read.
+ * @param encoding The character encoding.
+ * @return The string decoded from the {@code length} next bytes.
+ * @throws IOException If an error occurred while reading the bytes, or if the given encoding is invalid.
+ */
+ public final String readString(final int length, final String encoding) throws IOException {
+ if (buffer.hasArray() && length <= buffer.capacity()) {
+ ensureBufferContains(length);
+ final int position = buffer.position(); // Must be after 'ensureBufferContains(int)'.
+ buffer.position(position + length); // Before 'new String' for consistency with the 'else' block in case of UnsupportedEncodingException.
+ return new String(buffer.array(), buffer.arrayOffset() + position, length, encoding);
+ } else {
+ return new String(readBytes(length), encoding);
+ }
+ }
+
+ /**
* Moves to the given position in the stream, relative to the stream position at construction time.
*
* @param position The position where to move.
Modified: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreConnection.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreConnection.java?rev=1489435&r1=1489434&r2=1489435&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreConnection.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreConnection.java [UTF-8] Tue Jun 4 13:18:34 2013
@@ -51,9 +51,13 @@ import org.apache.sis.setup.OptionKey;
* <li>Any other {@code DataStore}-specific object, for example {@link ucar.nc2.NetcdfFile}.</li>
* </ul>
*
- * This class is used only for discovery of a {@code DataStore} implementation capable to handle the input.
+ * The {@link #getStorageAs(Class)} method provides the storage as an object of the given type, opening
+ * the input stream if necessary. This class tries to open the stream only once - subsequent invocation
+ * of {@code getStorageAs(…)} may return the same input stream.
+ *
+ * <p>This class is used only for discovery of a {@code DataStore} implementation capable to handle the input.
* Once a suitable {@code DataStore} has been found, the {@code DataStoreConnection} instance is typically
- * discarded since each data store implementation will use their own input/output objects.
+ * discarded since each data store implementation will use their own input/output objects.</p>
*
* <p>Instances of this class are serializable if the {@code storage} object given at construction time
* is serializable.</p>
@@ -112,9 +116,10 @@ public class DataStoreConnection impleme
* </ul>
*
* A non-existent entry means that the value has not yet been computed. A {@link Void#TYPE} value means the value
- * has been computed and we have determined that {@link #openAs(Class)} shall returns {@code null} for that type.
+ * has been computed and we have determined that {@link #getStorageAs(Class)} shall returns {@code null} for that
+ * type.
*
- * @see #openAs(Class)
+ * @see #getStorageAs(Class)
*/
private transient Map<Class<?>, Object> views;
@@ -171,6 +176,8 @@ public class DataStoreConnection impleme
* The object can be of any type, but the class javadoc lists the most typical ones.
*
* @return The input/output object as a URL, file, image input stream, <i>etc.</i>.
+ *
+ * @see #getStorageAs(Class)
*/
public Object getStorage() {
return storage;
@@ -228,43 +235,48 @@ public class DataStoreConnection impleme
* The default implementation accepts the following types:
*
* <ul>
- * <li><p>{@link ByteBuffer} -
- * A read-only view of the first bytes of the input stream, or {@code null} if unavailable.
- * If non-null, then the buffer can be used for a quick check of file magic number.</p></li>
- *
- * <li><p>{@link DataInput} -
- * Performs the following choice based on the type of the {@linkplain #getStorage() storage} object:
- * <ul>
- * <li>If the storage is already an instance of {@link DataInput} (including the {@link ImageInputStream}
- * and {@link javax.imageio.stream.ImageOutputStream} types), then it is returned unchanged.</li>
- *
- * <li>Otherwise if the input is an instance of {@link java.nio.file.Path}, {@link java.io.File},
- * {@link java.net.URI}, {@link java.net.URL}, {@link CharSequence}, {@link java.io.InputStream} or
- * {@link java.nio.channels.ReadableByteChannel}, then an {@link ImageInputStream} backed by a
- * {@link ByteBuffer} is created when first needed and returned.</li>
- *
- * <li>Otherwise if {@link ImageIO#createImageInputStream(Object)} returns a non-null value,
- * then this value is cached and returned.</li>
- *
- * <li>Otherwise this method returns {@code null}.</li>
- * </ul></p></li>
- *
- * <li><p>{@link Connection} -
- * Performs the following choice based on the type of the {@linkplain #getStorage() storage} object:
- * <ul>
- * <li>If the storage is already an instance of {@link Connection}, then it is returned unchanged.</li>
- *
- * <li>Otherwise if the storage is an instance of {@link DataSource}, then a connection is obtained
- * when first needed and returned.</li>
- *
- * <li>Otherwise this method returns {@code null}.</li>
- * </ul></p></li>
+ * <li>{@link ByteBuffer}:
+ * <ul>
+ * <li>If the {@linkplain #getStorage() storage} object can be obtained as described in bullet 2 of the
+ * {@code DataInput} section below, then this method returns the associated byte buffer.</li>
+ *
+ * <li>Otherwise this method returns {@code null}.</li>
+ * </ul>
+ * </li>
+ * <li>{@link DataInput}:
+ * <ul>
+ * <li>If the {@linkplain #getStorage() storage} object is already an instance of {@link DataInput}
+ * (including the {@link ImageInputStream} and {@link javax.imageio.stream.ImageOutputStream} types),
+ * then it is returned unchanged.</li>
+ *
+ * <li>Otherwise if the input is an instance of {@link java.nio.file.Path}, {@link java.io.File},
+ * {@link java.net.URI}, {@link java.net.URL}, {@link CharSequence}, {@link java.io.InputStream} or
+ * {@link java.nio.channels.ReadableByteChannel}, then an {@link ImageInputStream} backed by a
+ * {@link ByteBuffer} is created when first needed and returned.</li>
+ *
+ * <li>Otherwise if {@link ImageIO#createImageInputStream(Object)} returns a non-null value,
+ * then this value is cached and returned.</li>
+ *
+ * <li>Otherwise this method returns {@code null}.</li>
+ * </ul>
+ * </li>
+ * <li>{@link Connection}:
+ * <ul>
+ * <li>If the {@linkplain #getStorage() storage} object is already an instance of {@link Connection},
+ * then it is returned unchanged.</li>
+ *
+ * <li>Otherwise if the storage is an instance of {@link DataSource}, then a connection is obtained
+ * when first needed and returned.</li>
+ *
+ * <li>Otherwise this method returns {@code null}.</li>
+ * </ul>
+ * </li>
* </ul>
*
* Multiple invocations of this method on the same {@code DataStoreConnection} instance will try
* to return the same instance on a <cite>best effort</cite> basis. Consequently, implementations
- * of {@link DataStoreProvider#canOpen(DataStoreConnection)} methods shall not close the stream or
- * database connection returned by this method. In addition, those {@code canOpen(DataStoreConnection)}
+ * of {@link DataStoreProvider#canRead(DataStoreConnection)} methods shall not close the stream or
+ * database connection returned by this method. In addition, those {@code canRead(DataStoreConnection)}
* methods are responsible for restoring the stream or byte buffer to its original position on return.
*
* @param <T> The compile-time type of the {@code type} argument.
@@ -274,9 +286,10 @@ public class DataStoreConnection impleme
* @throws IllegalArgumentException If the given {@code type} argument is not a known type.
* @throws DataStoreException If an error occurred while opening a stream or database connection.
*
+ * @see #getStorage()
* @see #closeAllExcept(Object)
*/
- public <T> T openAs(final Class<T> type) throws IllegalArgumentException, DataStoreException {
+ public <T> T getStorageAs(final Class<T> type) throws IllegalArgumentException, DataStoreException {
ArgumentChecks.ensureNonNull("type", type);
if (views != null) {
final Object view = views.get(type);
@@ -316,9 +329,9 @@ public class DataStoreConnection impleme
/**
* Creates a view for the input as a {@link DataInput} if possible. This method performs the choice
- * documented in the {@link #openAs(Class)} method for the {@code DataInput} case. Opening the data
- * input may imply creating a {@link ByteBuffer}, in which case the buffer will be stored under the
- * {@code ByteBuffer.class} key together with the {@code DataInput.class} case.
+ * documented in the {@link #getStorageAs(Class)} method for the {@code DataInput} case. Opening the
+ * data input may imply creating a {@link ByteBuffer}, in which case the buffer will be stored under
+ * the {@code ByteBuffer.class} key together with the {@code DataInput.class} case.
*
* @throws IOException If an error occurred while opening a stream for the input.
*/
@@ -372,7 +385,7 @@ public class DataStoreConnection impleme
/**
* Creates a storage view of the given type if possible, or returns {@code null} otherwise.
- * This method is invoked by {@link #openAs(Class)} when first needed, and the result is cached.
+ * This method is invoked by {@link #getStorageAs(Class)} when first needed, and the result is cached.
*
* @param <T> The compile-time type of the {@code type} argument.
* @param type The type of the view to create.
@@ -418,7 +431,7 @@ public class DataStoreConnection impleme
/**
* Closes all streams and connections created by this {@code DataStoreConnection} except the given view.
- * This method closes all objects created by the {@link #openAs(Class)} method, leaving only {@code view} open.
+ * This method closes all objects created by the {@link #getStorageAs(Class)} method except the given {@code view}.
* If {@code view} is {@code null}, then this method closes everything including the {@linkplain #getStorage()
* storage} if it is closeable.
*
@@ -431,7 +444,7 @@ public class DataStoreConnection impleme
* @param view The view to leave open, or {@code null} if none.
* @throws DataStoreException If an error occurred while closing the stream or database connection.
*
- * @see #openAs(Class)
+ * @see #getStorageAs(Class)
*/
public void closeAllExcept(final Object view) throws DataStoreException {
boolean close = (view == null);
Modified: sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java?rev=1489435&r1=1489434&r2=1489435&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java [UTF-8] Tue Jun 4 13:18:34 2013
@@ -143,6 +143,23 @@ public final strictfp class ChannelDataI
}
/**
+ * Tests the {@link ChannelDataInput#readString(int, String)} method.
+ *
+ * @throws IOException Should never happen.
+ */
+ @Test
+ public void testReadString() throws IOException {
+ final String expected = "お元気ですか";
+ final byte[] array = expected.getBytes("UTF-8");
+ assertEquals(expected.length()*3, array.length); // Sanity check.
+ final ChannelDataInput input = new ChannelDataInput("testReadString",
+ Channels.newChannel(new ByteArrayInputStream(array)),
+ ByteBuffer.allocate(array.length + 4), false);
+ assertEquals(expected, input.readString(array.length, "UTF-8"));
+ assertFalse(input.buffer.hasRemaining());
+ }
+
+ /**
* Tests {@link ChannelDataInput#seek(long)} on a channel that do not implement
* {@link java.nio.channels.SeekableByteChannel}.
*
Modified: sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreConnectionTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreConnectionTest.java?rev=1489435&r1=1489434&r2=1489435&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreConnectionTest.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreConnectionTest.java [UTF-8] Tue Jun 4 13:18:34 2013
@@ -75,7 +75,7 @@ public final strictfp class DataStoreCon
}
/**
- * Tests the {@link DataStoreConnection#openAs(Class)} method for the I/O types.
+ * Tests the {@link DataStoreConnection#getStorageAs(Class)} method for the I/O types.
* The initial storage object is a {@link java.net.URL}.
*
* @throws DataStoreException Should never happen.
@@ -87,7 +87,7 @@ public final strictfp class DataStoreCon
}
/**
- * Tests the {@link DataStoreConnection#openAs(Class)} method for the I/O types.
+ * Tests the {@link DataStoreConnection#getStorageAs(Class)} method for the I/O types.
* The initial storage object is an {@link java.io.InputStream}.
*
* @throws DataStoreException Should never happen.
@@ -103,8 +103,8 @@ public final strictfp class DataStoreCon
*/
private void testOpenAsDataInput(final boolean asStream) throws DataStoreException, IOException {
final DataStoreConnection connection = create(asStream);
- final DataInput input = connection.openAs(DataInput.class);
- assertSame("Value shall be cached.", input, connection.openAs(DataInput.class));
+ final DataInput input = connection.getStorageAs(DataInput.class);
+ assertSame("Value shall be cached.", input, connection.getStorageAs(DataInput.class));
assertInstanceOf("Needs the SIS implementation", ChannelImageInputStream.class, input);
final ReadableByteChannel channel = ((ChannelImageInputStream) input).channel;
/*
@@ -118,7 +118,7 @@ public final strictfp class DataStoreCon
}
/**
- * Tests the {@link DataStoreConnection#openAs(Class)} method for the {@link ByteBuffer} type.
+ * Tests the {@link DataStoreConnection#getStorageAs(Class)} method for the {@link ByteBuffer} type.
* This method uses the same test file than {@link #testOpenFromURL()}.
*
* @throws DataStoreException Should never happen.
@@ -128,7 +128,7 @@ public final strictfp class DataStoreCon
@DependsOnMethod("testOpenFromURL")
public void testOpenAsByteBuffer() throws DataStoreException, IOException {
final DataStoreConnection connection = create(false);
- final ByteBuffer buffer = connection.openAs(ByteBuffer.class);
+ final ByteBuffer buffer = connection.getStorageAs(ByteBuffer.class);
assertEquals(MAGIC_NUMBER, buffer.getInt());
connection.closeAllExcept(null);
}
@@ -143,7 +143,7 @@ public final strictfp class DataStoreCon
@DependsOnMethod("testOpenFromStream")
public void testCloseAllExcept() throws DataStoreException, IOException {
final DataStoreConnection connection = create(true);
- final DataInput input = connection.openAs(DataInput.class);
+ final DataInput input = connection.getStorageAs(DataInput.class);
final ReadableByteChannel channel = ((ChannelImageInputStream) input).channel;
assertTrue("channel.isOpen()", channel.isOpen());
connection.closeAllExcept(input);