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);