garydgregory commented on code in PR #784:
URL: https://github.com/apache/commons-io/pull/784#discussion_r2370318884


##########
src/main/java/org/apache/commons/io/build/AbstractOrigin.java:
##########
@@ -755,6 +1120,28 @@ public Writer getWriter(final Charset charset, final 
OpenOption... options) thro
         return Files.newBufferedWriter(getPath(), Charsets.toCharset(charset), 
options);
     }
 
+    /**
+     * Gets this origin as a Channel, if possible.
+     *
+     * @param channelType The type of channel to return.
+     * @param options Options specifying how a file-based origin is opened, 
ignored otherwise.
+     * @return A new Channel on the origin of the given type.
+     * @param <C> The type of channel to return.
+     * @throws IOException                   If an I/O error occurs.
+     * @throws UnsupportedOperationException If this origin cannot be 
converted to a channel of the given type.
+     * @since 2.21.0
+     */
+    public <C extends Channel> C getChannel(Class<C> channelType, 
OpenOption... options) throws IOException {
+        Objects.requireNonNull(channelType, "channelType");
+        final Channel channel = doGetChannel(options);
+        if (channelType.isInstance(channel)) {
+            return channelType.cast(channel);
+        }
+        throw unsupportedChannelType(channelType);
+    }
+
+    abstract Channel doGetChannel(OpenOption... options) throws IOException;

Review Comment:
   What is this supposed to do? A Javadoc would help 😉 
   
   I'm _really_ not a fan of the `doFoo` naming convention. To me it screams: 
"I couldn't think of a better name!". It looks like all of these 
implementations create a _new_ channel (except `ChannelOrigin` obviously), 
so... how about  calling it`newChannel()`? At least that name is pretty clear, 
even without a Javadoc 😉 
   



##########
src/main/java/org/apache/commons/io/channels/ByteArraySeekableByteChannel.java:
##########
@@ -37,41 +37,74 @@
  * accessed via {@link ByteArraySeekableByteChannel#array()}.
  * </p>
  *
- * @since 2.19.0
+ * @since 2.21.0
  */
 public class ByteArraySeekableByteChannel implements SeekableByteChannel {
 
     private static final int RESIZE_LIMIT = Integer.MAX_VALUE >> 1;
+
+    /**
+     * Constructs a new channel backed directly by the given byte array.
+     *
+     * <p>The channel initially contains the full contents of the array, with 
its
+     * size set to {@code bytes.length} and its position set to {@code 0}.</p>
+     *
+     * <p>Reads and writes operate on the shared array.
+     * If a write operation extends beyond the current capacity, the channel 
will
+     * automatically allocate a larger backing array and copy the existing 
contents.</p>
+     *
+     * @param bytes The byte array to wrap, must not be {@code null}
+     * @return A new channel that uses the given array as its initial backing 
store
+     * @throws NullPointerException If {@code bytes} is {@code null}
+     * @see #array()
+     * @see java.io.ByteArrayInputStream#ByteArrayInputStream(byte[])
+     */
+    public static ByteArraySeekableByteChannel wrap(byte[] bytes) {
+        Objects.requireNonNull(bytes, "bytes");
+        return new ByteArraySeekableByteChannel(bytes);
+    }
+
     private byte[] data;
-    private final AtomicBoolean closed = new AtomicBoolean();
+    private volatile boolean closed;
     private int position;
     private int size;
     private final ReentrantLock lock = new ReentrantLock();
 
     /**
-     * Constructs a new instance using a default empty buffer.
+     * Constructs a new instance, with a default internal buffer capacity.
+     * <p>
+     * The initial size and position of the channel are 0.
+     * </p>
+     * <p>
+     * The default initial capacity is 32 bytes, although the capacity will 
grow as needed when writing data.
+     * </p>
+     * @see java.io.ByteArrayOutputStream#ByteArrayOutputStream()

Review Comment:
   There should be a blank Javadoc line before the first `@` tag.
   
   Don't use FQCN when you don't need to in `@see java.io...`



##########
src/main/java/org/apache/commons/io/build/AbstractOrigin.java:
##########
@@ -53,15 +58,246 @@
 import org.apache.commons.io.output.WriterOutputStream;
 
 /**
- * Abstracts the origin of data for builders like a {@link File}, {@link 
Path}, {@link Reader}, {@link Writer}, {@link InputStream}, {@link 
OutputStream}, and
- * {@link URI}.
+ * Abstract base class that encapsulates the <em>origin</em> of data used by 
Commons IO builders.
  * <p>
- * Some methods may throw {@link UnsupportedOperationException} if that method 
is not implemented in a concrete subclass, see {@link #getFile()} and
- * {@link #getPath()}.
+ * An origin represents where bytes/characters come from or go to, such as a 
{@link File}, {@link Path},
+ * {@link Reader}, {@link Writer}, {@link InputStream}, {@link OutputStream}, 
or {@link URI}. Concrete subclasses
+ * expose only the operations that make sense for the underlying source or 
sink; invoking an unsupported operation
+ * results in {@link UnsupportedOperationException} (see, for example, {@link 
#getFile()} and {@link #getPath()}).
  * </p>
  *
- * @param <T> the type of instances to build.
- * @param <B> the type of builder subclass.
+ * <p>
+ * The table below summarizes which views and conversions are supported for 
each origin type.
+ * Column headers show the target view; cells indicate whether that view is 
available from the origin in that row.
+ * </p>
+ *
+ * <table>
+ *   <caption>Origin support matrix</caption>

Review Comment:
   Nice! I see entries for `WritableByteChannel`, but shouldn't there be 
entries for `ReadableByteChannel`, and `SeekableByteChannel`? 
   
   Or should there be a single entry for `Channel`?



##########
src/main/java/org/apache/commons/io/channels/ByteArraySeekableByteChannel.java:
##########
@@ -37,41 +37,74 @@
  * accessed via {@link ByteArraySeekableByteChannel#array()}.
  * </p>
  *
- * @since 2.19.0
+ * @since 2.21.0

Review Comment:
   Good catch! 👍 



##########
src/main/java/org/apache/commons/io/channels/ByteArraySeekableByteChannel.java:
##########
@@ -37,41 +37,74 @@
  * accessed via {@link ByteArraySeekableByteChannel#array()}.
  * </p>
  *
- * @since 2.19.0
+ * @since 2.21.0
  */
 public class ByteArraySeekableByteChannel implements SeekableByteChannel {
 
     private static final int RESIZE_LIMIT = Integer.MAX_VALUE >> 1;
+
+    /**
+     * Constructs a new channel backed directly by the given byte array.
+     *
+     * <p>The channel initially contains the full contents of the array, with 
its
+     * size set to {@code bytes.length} and its position set to {@code 0}.</p>
+     *
+     * <p>Reads and writes operate on the shared array.
+     * If a write operation extends beyond the current capacity, the channel 
will
+     * automatically allocate a larger backing array and copy the existing 
contents.</p>
+     *
+     * @param bytes The byte array to wrap, must not be {@code null}
+     * @return A new channel that uses the given array as its initial backing 
store
+     * @throws NullPointerException If {@code bytes} is {@code null}
+     * @see #array()
+     * @see java.io.ByteArrayInputStream#ByteArrayInputStream(byte[])
+     */
+    public static ByteArraySeekableByteChannel wrap(byte[] bytes) {
+        Objects.requireNonNull(bytes, "bytes");
+        return new ByteArraySeekableByteChannel(bytes);
+    }
+
     private byte[] data;
-    private final AtomicBoolean closed = new AtomicBoolean();
+    private volatile boolean closed;
     private int position;
     private int size;
     private final ReentrantLock lock = new ReentrantLock();
 
     /**
-     * Constructs a new instance using a default empty buffer.
+     * Constructs a new instance, with a default internal buffer capacity.
+     * <p>
+     * The initial size and position of the channel are 0.
+     * </p>
+     * <p>
+     * The default initial capacity is 32 bytes, although the capacity will 
grow as needed when writing data.
+     * </p>
+     * @see java.io.ByteArrayOutputStream#ByteArrayOutputStream()
      */
     public ByteArraySeekableByteChannel() {
-        this(0);
+        this(32);
     }
 
     /**
-     * Constructs a new instance from a byte array.
-     *
-     * @param data input data or pre-allocated array.
+     * Constructs a new instance, with an internal buffer of the given 
capacity, in bytes.
+     * <p>
+     * The initial size and position of the channel are 0.
+     * </p>
+     * @param size Capacity of the internal buffer to allocate, in bytes.
+     * @see java.io.ByteArrayOutputStream#ByteArrayOutputStream(int)

Review Comment:
   Don't use FQCN when you don't need to in `@see java.io...`



##########
src/main/java/org/apache/commons/io/build/AbstractStreamBuilder.java:
##########
@@ -258,6 +260,23 @@ public Writer getWriter() throws IOException {
         return checkOrigin().getWriter(getCharset(), getOpenOptions());
     }
 
+    /**
+     * Gets a Channel from the origin with OpenOption[].
+     *
+     * @param channelType The channel type, not null.
+     * @return A channel of the specified type.
+     * @param <C>         The channel type.
+     * @throws IllegalStateException         if the {@code origin} is {@code 
null}.
+     * @throws UnsupportedOperationException if the origin cannot be converted 
to a {@link ReadableByteChannel}.
+     * @throws IOException                   if an I/O error occurs.
+     * @see AbstractOrigin#getChannel
+     * @see #getOpenOptions()
+     * @since 2.21.0
+     */
+    public <C extends Channel> C getChannel(Class<C> channelType) throws 
IOException {

Review Comment:
   Yeah, that's likely a good general solution 👍 



##########
src/main/java/org/apache/commons/io/channels/ByteArraySeekableByteChannel.java:
##########
@@ -37,41 +37,74 @@
  * accessed via {@link ByteArraySeekableByteChannel#array()}.
  * </p>
  *
- * @since 2.19.0
+ * @since 2.21.0
  */
 public class ByteArraySeekableByteChannel implements SeekableByteChannel {
 
     private static final int RESIZE_LIMIT = Integer.MAX_VALUE >> 1;
+
+    /**
+     * Constructs a new channel backed directly by the given byte array.
+     *
+     * <p>The channel initially contains the full contents of the array, with 
its
+     * size set to {@code bytes.length} and its position set to {@code 0}.</p>
+     *
+     * <p>Reads and writes operate on the shared array.
+     * If a write operation extends beyond the current capacity, the channel 
will
+     * automatically allocate a larger backing array and copy the existing 
contents.</p>
+     *
+     * @param bytes The byte array to wrap, must not be {@code null}
+     * @return A new channel that uses the given array as its initial backing 
store
+     * @throws NullPointerException If {@code bytes} is {@code null}
+     * @see #array()
+     * @see java.io.ByteArrayInputStream#ByteArrayInputStream(byte[])
+     */
+    public static ByteArraySeekableByteChannel wrap(byte[] bytes) {
+        Objects.requireNonNull(bytes, "bytes");
+        return new ByteArraySeekableByteChannel(bytes);
+    }
+
     private byte[] data;
-    private final AtomicBoolean closed = new AtomicBoolean();
+    private volatile boolean closed;
     private int position;
     private int size;
     private final ReentrantLock lock = new ReentrantLock();
 
     /**
-     * Constructs a new instance using a default empty buffer.
+     * Constructs a new instance, with a default internal buffer capacity.
+     * <p>
+     * The initial size and position of the channel are 0.
+     * </p>
+     * <p>
+     * The default initial capacity is 32 bytes, although the capacity will 
grow as needed when writing data.
+     * </p>
+     * @see java.io.ByteArrayOutputStream#ByteArrayOutputStream()
      */
     public ByteArraySeekableByteChannel() {
-        this(0);
+        this(32);

Review Comment:
   Hm, why not `IOUtils.DEFAULT_BUFFER_SIZE` (8 KiB)? Confusingly we use `1024` 
in our own `AbstractByteArrayOutputStream` and `ByteArrayOutputStream`.
   
   I'm surprised this change doesn't break anything, because if you only make 
that change, `ByteArraySeekableByteChannelTest` breaks.
   When I tried this kinf of change here and in Compress' 
`SeekableInMemoryByteChannel`, the tests broke when I changed the default size 
to `IOUtils.DEFAULT_BUFFER_SIZE`. But now it works in 
`SeekableInMemoryByteChannel`... 



##########
src/main/java/org/apache/commons/io/channels/ByteArraySeekableByteChannel.java:
##########
@@ -37,41 +37,74 @@
  * accessed via {@link ByteArraySeekableByteChannel#array()}.
  * </p>
  *
- * @since 2.19.0
+ * @since 2.21.0
  */
 public class ByteArraySeekableByteChannel implements SeekableByteChannel {
 
     private static final int RESIZE_LIMIT = Integer.MAX_VALUE >> 1;
+
+    /**
+     * Constructs a new channel backed directly by the given byte array.
+     *
+     * <p>The channel initially contains the full contents of the array, with 
its
+     * size set to {@code bytes.length} and its position set to {@code 0}.</p>
+     *
+     * <p>Reads and writes operate on the shared array.
+     * If a write operation extends beyond the current capacity, the channel 
will
+     * automatically allocate a larger backing array and copy the existing 
contents.</p>
+     *
+     * @param bytes The byte array to wrap, must not be {@code null}
+     * @return A new channel that uses the given array as its initial backing 
store
+     * @throws NullPointerException If {@code bytes} is {@code null}
+     * @see #array()
+     * @see java.io.ByteArrayInputStream#ByteArrayInputStream(byte[])
+     */
+    public static ByteArraySeekableByteChannel wrap(byte[] bytes) {
+        Objects.requireNonNull(bytes, "bytes");
+        return new ByteArraySeekableByteChannel(bytes);
+    }
+
     private byte[] data;
-    private final AtomicBoolean closed = new AtomicBoolean();
+    private volatile boolean closed;

Review Comment:
   This seems unrelated. Why is this better? Just because it's simpler? Just 
curious.



##########
src/test/java/org/apache/commons/io/channels/ByteArraySeekableByteChannelCompressTest.java:
##########
@@ -62,7 +66,7 @@ void testCloseIsIdempotent() throws Exception {
     @ParameterizedTest
     @ValueSource(ints = { 0, 1, 2, 3, 4, 5, 6 })
     void testReadingFromAPositionAfterEndReturnsEOF(final int size) throws 
Exception {
-        try (SeekableByteChannel c = new ByteArraySeekableByteChannel(size)) {
+        try (SeekableByteChannel c = ByteArraySeekableByteChannel.wrap(new 
byte[size])) {

Review Comment:
   I don't think this is a good idea for reading tests. You want to test 
reading from the same array over and over when you are reading because you want 
to make sure there are no side-effects that incorrectly _write_ to the 
underlying byte array.



##########
src/test/java/org/apache/commons/io/channels/ByteArraySeekableByteChannelCompressTest.java:
##########
@@ -42,6 +42,10 @@ class ByteArraySeekableByteChannelCompressTest {
 
     private final byte[] testData = "Some 
data".getBytes(StandardCharsets.UTF_8);
 
+    private byte[] getTestData() {

Review Comment:
   This would be better named `cloneTestData` IMO to make clear to tests what 
kind of data we are dealing with.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to