From bb3226a54ba0eb9d261589e989f0c1e422bf65dc Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz" This method is functionally equivalent to
+ * {@code java.util.Objects#checkFromIndexSize(int, int, int)} introduced in Java 9,
+ * but is provided here for use on Java 8. The range is valid if all of the following hold: If the range is invalid, this method throws an
+ * {@link IndexOutOfBoundsException} with a descriptive message. The range is valid if all of the following hold: If the range is invalid, this method throws an
+ * {@link IndexOutOfBoundsException} with a descriptive message. The range is valid if all of the following hold: If the range is invalid, this method throws an
+ * {@link IndexOutOfBoundsException} with a descriptive message. This method is functionally equivalent to
- * {@code java.util.Objects#checkFromIndexSize(int, int, int)} introduced in Java 9,
- * but is provided here for use on Java 8. The range is valid if all of the following hold: If the range is invalid, this method throws an
* {@link IndexOutOfBoundsException} with a descriptive message.
+ *
+ *
+ *
diff --git a/src/test/java/org/apache/commons/io/IOUtilsTest.java b/src/test/java/org/apache/commons/io/IOUtilsTest.java
index bae8f59e374..d062ae61ea8 100644
--- a/src/test/java/org/apache/commons/io/IOUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/IOUtilsTest.java
@@ -50,6 +50,7 @@
import java.io.SequenceInputStream;
import java.io.StringReader;
import java.io.Writer;
+import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
@@ -65,6 +66,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -85,7 +87,9 @@
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
import org.apache.commons.io.test.TestUtils;
import org.apache.commons.io.test.ThrowOnCloseReader;
+import org.apache.commons.lang3.JavaVersion;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@@ -348,6 +352,54 @@ void testByteArrayWithNegativeSize() {
assertThrows(NegativeArraySizeException.class, () -> IOUtils.byteArray(-1));
}
+ static Stream
+ *
+ *
+ *
+ *
+ *
+ *
*
*
* The range is valid if all of the following hold:
If the range is invalid, this method throws an {@link IndexOutOfBoundsException} with a descriptive message.
+ * + * @param seq The char sequence to be checked against + * @param fromIndex The starting index into the char sequence (inclusive) + * @param toIndex The ending index into the char sequence (exclusive) + * @throws NullPointerException If the char sequence is null + * @throws IndexOutOfBoundsException If the range {@code [fromIndex, toIndex)} is out of bounds + * @since 2.21.0 + */ + public static void checkFromToIndex(final CharSequence seq, int fromIndex, final int toIndex) { + checkFromToIndex(fromIndex, toIndex, requireNonNull(seq, "char sequence").length()); + } + + static void checkFromToIndex(final int fromIndex, final int toIndex, final int length) { + if (fromIndex < 0 | toIndex < fromIndex | length < toIndex) { + throw new IndexOutOfBoundsException(String.format("Range [%s, %s) out of bounds for length %s", fromIndex, toIndex, length)); + } + } + /** * Clears any state. *
@@ -292,7 +294,8 @@ public int read(final byte[] bytes) throws IOException {
*/
@Override
public int read(final byte[] bytes, final int offset, final int length) throws IOException {
- if (bytes.length == 0 || length == 0) {
+ IOUtils.checkFromIndexSize(bytes, offset, length);
+ if (length == 0) {
return 0;
}
checkOpen();
diff --git a/src/main/java/org/apache/commons/io/input/NullReader.java b/src/main/java/org/apache/commons/io/input/NullReader.java
index c5ae530a170..2b7fdb47175 100644
--- a/src/main/java/org/apache/commons/io/input/NullReader.java
+++ b/src/main/java/org/apache/commons/io/input/NullReader.java
@@ -22,6 +22,8 @@
import java.io.IOException;
import java.io.Reader;
+import org.apache.commons.io.IOUtils;
+
/**
* A functional, lightweight {@link Reader} that emulates
* a reader of a specified size.
@@ -276,6 +278,10 @@ public int read(final char[] chars) throws IOException {
*/
@Override
public int read(final char[] chars, final int offset, final int length) throws IOException {
+ IOUtils.checkFromIndexSize(chars, offset, length);
+ if (length == 0) {
+ return 0;
+ }
if (eof) {
throw new IOException("Read after end of file");
}
diff --git a/src/main/java/org/apache/commons/io/input/QueueInputStream.java b/src/main/java/org/apache/commons/io/input/QueueInputStream.java
index 1515eb94036..5f701945cb3 100644
--- a/src/main/java/org/apache/commons/io/input/QueueInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/QueueInputStream.java
@@ -30,6 +30,7 @@
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.io.build.AbstractStreamBuilder;
import org.apache.commons.io.output.QueueOutputStream;
@@ -244,13 +245,7 @@ public int read() {
*/
@Override
public int read(final byte[] b, final int offset, final int length) {
- if (b == null) {
- throw new NullPointerException();
- }
- if (offset < 0 || length < 0 || length > b.length - offset) {
- throw new IndexOutOfBoundsException(
- String.format("Range [%d, %
@@ -124,10 +126,7 @@ public T getAppendable() {
*/
@Override
public void write(final char[] cbuf, final int off, final int len) throws IOException {
- Objects.requireNonNull(cbuf, "cbuf");
- if (len < 0 || off + len > cbuf.length) {
- throw new IndexOutOfBoundsException("Array Size=" + cbuf.length + ", offset=" + off + ", length=" + len);
- }
+ IOUtils.checkFromIndexSize(cbuf, off, len);
for (int i = 0; i < len; i++) {
appendable.append(cbuf[off + i]);
}
diff --git a/src/main/java/org/apache/commons/io/output/BrokenWriter.java b/src/main/java/org/apache/commons/io/output/BrokenWriter.java
index 7fe244d2392..1feac6bb84a 100644
--- a/src/main/java/org/apache/commons/io/output/BrokenWriter.java
+++ b/src/main/java/org/apache/commons/io/output/BrokenWriter.java
@@ -20,6 +20,7 @@
import java.io.Writer;
import java.util.function.Supplier;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.io.function.Erase;
/**
@@ -121,6 +122,7 @@ private RuntimeException rethrow() {
*/
@Override
public void write(final char[] cbuf, final int off, final int len) throws IOException {
+ IOUtils.checkFromIndexSize(cbuf, off, len);
throw rethrow();
}
diff --git a/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java b/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java
index b2a9afbfc58..48fb793669b 100644
--- a/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java
@@ -21,6 +21,8 @@
import java.io.InputStream;
import java.io.OutputStream;
+import org.apache.commons.io.IOUtils;
+
/**
* Implements a ThreadSafe version of {@link AbstractByteArrayOutputStream} using instance synchronization.
*/
@@ -130,13 +132,7 @@ public synchronized InputStream toInputStream() {
@Override
public void write(final byte[] b, final int off, final int len) {
- if (off < 0
- || off > b.length
- || len < 0
- || off + len > b.length
- || off + len < 0) {
- throw new IndexOutOfBoundsException();
- }
+ IOUtils.checkFromIndexSize(b, off, len);
if (len == 0) {
return;
}
diff --git a/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java b/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java
index 4612a7de2f7..adecd946a3e 100644
--- a/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java
@@ -172,6 +172,7 @@ int getChunkSize() {
*/
@Override
public void write(final byte[] data, final int srcOffset, final int length) throws IOException {
+ IOUtils.checkFromIndexSize(data, srcOffset, length);
int bytes = length;
int dstOffset = srcOffset;
while (bytes > 0) {
diff --git a/src/main/java/org/apache/commons/io/output/ChunkedWriter.java b/src/main/java/org/apache/commons/io/output/ChunkedWriter.java
index 6a6492882b1..f18553fb776 100644
--- a/src/main/java/org/apache/commons/io/output/ChunkedWriter.java
+++ b/src/main/java/org/apache/commons/io/output/ChunkedWriter.java
@@ -74,6 +74,7 @@ public ChunkedWriter(final Writer writer, final int chunkSize) {
*/
@Override
public void write(final char[] data, final int srcOffset, final int length) throws IOException {
+ IOUtils.checkFromIndexSize(data, srcOffset, length);
int bytes = length;
int dstOffset = srcOffset;
while (bytes > 0) {
diff --git a/src/main/java/org/apache/commons/io/output/ClosedOutputStream.java b/src/main/java/org/apache/commons/io/output/ClosedOutputStream.java
index 18783481d44..faa342f40f6 100644
--- a/src/main/java/org/apache/commons/io/output/ClosedOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ClosedOutputStream.java
@@ -20,6 +20,8 @@
import java.io.IOException;
import java.io.OutputStream;
+import org.apache.commons.io.IOUtils;
+
/**
* Throws an IOException on all attempts to write to the stream.
*
@@ -73,6 +75,7 @@ public void flush() throws IOException {
*/
@Override
public void write(final byte b[], final int off, final int len) throws IOException {
+ IOUtils.checkFromIndexSize(b, off, len);
throw new IOException("write(byte[], int, int) failed: stream is closed");
}
diff --git a/src/main/java/org/apache/commons/io/output/ClosedWriter.java b/src/main/java/org/apache/commons/io/output/ClosedWriter.java
index 58c3993acd3..bc1add898ad 100644
--- a/src/main/java/org/apache/commons/io/output/ClosedWriter.java
+++ b/src/main/java/org/apache/commons/io/output/ClosedWriter.java
@@ -20,6 +20,8 @@
import java.io.IOException;
import java.io.Writer;
+import org.apache.commons.io.IOUtils;
+
/**
* Throws an IOException on all attempts to write with {@link #close()} implemented as a noop.
*
@@ -77,6 +79,7 @@ public void flush() throws IOException {
*/
@Override
public void write(final char[] cbuf, final int off, final int len) throws IOException {
+ IOUtils.checkFromIndexSize(cbuf, off, len);
throw new IOException("write(" + new String(cbuf) + ", " + off + ", " + len + ") failed: stream is closed");
}
}
diff --git a/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java b/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java
index d78f049e635..1a13d90eff3 100644
--- a/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java
+++ b/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java
@@ -28,6 +28,7 @@
import org.apache.commons.io.IOExceptionList;
import org.apache.commons.io.IOIndexedException;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.io.function.IOConsumer;
/**
@@ -128,6 +129,7 @@ public void write(final char[] cbuf) throws IOException {
@SuppressWarnings("resource") // no allocation
@Override
public void write(final char[] cbuf, final int off, final int len) throws IOException {
+ IOUtils.checkFromIndexSize(cbuf, off, len);
forAllWriters(w -> w.write(cbuf, off, len));
}
@@ -159,6 +161,7 @@ public void write(final String str) throws IOException {
@SuppressWarnings("resource") // no allocation
@Override
public void write(final String str, final int off, final int len) throws IOException {
+ IOUtils.checkFromIndexSize(str, off, len);
forAllWriters(w -> w.write(str, off, len));
}
diff --git a/src/main/java/org/apache/commons/io/output/NullAppendable.java b/src/main/java/org/apache/commons/io/output/NullAppendable.java
index 520daa3a64b..fee5dace602 100644
--- a/src/main/java/org/apache/commons/io/output/NullAppendable.java
+++ b/src/main/java/org/apache/commons/io/output/NullAppendable.java
@@ -19,6 +19,8 @@
import java.io.IOException;
+import org.apache.commons.io.IOUtils;
+
/**
* Appends all data to the famous /dev/null.
*
@@ -51,6 +53,7 @@ public Appendable append(final CharSequence csq) throws IOException {
@Override
public Appendable append(final CharSequence csq, final int start, final int end) throws IOException {
+ IOUtils.checkFromToIndex(csq, start, end);
return this;
}
diff --git a/src/main/java/org/apache/commons/io/output/NullOutputStream.java b/src/main/java/org/apache/commons/io/output/NullOutputStream.java
index 5275aea9c75..682d0f8e768 100644
--- a/src/main/java/org/apache/commons/io/output/NullOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/NullOutputStream.java
@@ -19,6 +19,8 @@
import java.io.IOException;
import java.io.OutputStream;
+import org.apache.commons.io.IOUtils;
+
/**
* Never writes data. Calls never go beyond this class.
*
@@ -75,7 +77,7 @@ public void write(final byte[] b) throws IOException {
*/
@Override
public void write(final byte[] b, final int off, final int len) {
- // noop
+ IOUtils.checkFromIndexSize(b, off, len);
}
/**
diff --git a/src/main/java/org/apache/commons/io/output/NullWriter.java b/src/main/java/org/apache/commons/io/output/NullWriter.java
index ab7f4ddd6f8..b209056d194 100644
--- a/src/main/java/org/apache/commons/io/output/NullWriter.java
+++ b/src/main/java/org/apache/commons/io/output/NullWriter.java
@@ -18,6 +18,8 @@
import java.io.Writer;
+import org.apache.commons.io.IOUtils;
+
/**
* Never writes data. Calls never go beyond this class.
*
@@ -87,7 +89,7 @@ public Writer append(final CharSequence csq) {
*/
@Override
public Writer append(final CharSequence csq, final int start, final int end) {
- //to /dev/null
+ IOUtils.checkFromToIndex(csq, start, end);
return this;
}
@@ -154,7 +156,7 @@ public void write(final String str) {
*/
@Override
public void write(final String str, final int st, final int end) {
- //to /dev/null
+ IOUtils.checkFromIndexSize(str, st, end);
}
}
diff --git a/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java b/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java
index 106525e88f9..836e8bc3753 100644
--- a/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java
+++ b/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java
@@ -20,6 +20,8 @@
import java.io.StringWriter;
import java.io.Writer;
+import org.apache.commons.io.IOUtils;
+
/**
* {@link Writer} implementation that outputs to a {@link StringBuilder}.
*
@@ -149,9 +151,8 @@ public String toString() {
*/
@Override
public void write(final char[] value, final int offset, final int length) {
- if (value != null) {
- builder.append(value, offset, length);
- }
+ IOUtils.checkFromIndexSize(value, offset, length);
+ builder.append(value, offset, length);
}
/**
diff --git a/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java b/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java
index e5892dbd885..2efe401910a 100644
--- a/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java
@@ -19,6 +19,7 @@
import java.io.IOException;
import java.io.OutputStream;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.io.function.IOConsumer;
import org.apache.commons.io.function.IOFunction;
@@ -249,6 +250,7 @@ public void write(final byte[] b) throws IOException {
@SuppressWarnings("resource") // the underlying stream is managed by a subclass.
@Override
public void write(final byte[] b, final int off, final int len) throws IOException {
+ IOUtils.checkFromIndexSize(b, off, len);
// TODO we could write the sub-array up the threshold, fire the event,
// and then write the rest so the event is always fired at the precise point.
checkThreshold(len);
diff --git a/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java b/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
index f20ccefad74..a47a3b2aa2a 100644
--- a/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
@@ -21,6 +21,7 @@
import java.io.InputStream;
import java.io.OutputStream;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.io.build.AbstractOrigin;
import org.apache.commons.io.build.AbstractStreamBuilder;
import org.apache.commons.io.function.Uncheck;
@@ -214,9 +215,7 @@ public InputStream toInputStream() {
@Override
public void write(final byte[] b, final int off, final int len) {
- if (off < 0 || off > b.length || len < 0 || off + len > b.length || off + len < 0) {
- throw new IndexOutOfBoundsException(String.format("offset=%,d, length=%,d", off, len));
- }
+ IOUtils.checkFromIndexSize(b, off, len);
if (len == 0) {
return;
}
diff --git a/src/main/java/org/apache/commons/io/output/WriterOutputStream.java b/src/main/java/org/apache/commons/io/output/WriterOutputStream.java
index dcb6e226e72..c753ccdce87 100644
--- a/src/main/java/org/apache/commons/io/output/WriterOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/WriterOutputStream.java
@@ -448,6 +448,7 @@ public void write(final byte[] b) throws IOException {
*/
@Override
public void write(final byte[] b, int off, int len) throws IOException {
+ IOUtils.checkFromIndexSize(b, off, len);
while (len > 0) {
final int c = Math.min(len, decoderIn.remaining());
decoderIn.put(b, off, c);
diff --git a/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java b/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java
index 40fdcc8f730..73630a4bca2 100644
--- a/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java
+++ b/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java
@@ -306,6 +306,7 @@ public String getEncoding() {
*/
@Override
public void write(final char[] cbuf, final int off, final int len) throws IOException {
+ IOUtils.checkFromIndexSize(cbuf, off, len);
if (prologWriter != null) {
detectEncoding(cbuf, off, len);
} else {
diff --git a/src/test/java/org/apache/commons/io/IOUtilsTest.java b/src/test/java/org/apache/commons/io/IOUtilsTest.java
index d062ae61ea8..02574946bf8 100644
--- a/src/test/java/org/apache/commons/io/IOUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/IOUtilsTest.java
@@ -400,6 +400,56 @@ void testCheckFromIndexSizeInvalidCases(int off, int len, int arrayLength) {
}
}
+ static Stream
If {@code seq} is null, it is treated as the string "null".
+ * *If the range is invalid, this method throws an {@link IndexOutOfBoundsException} with a descriptive message.
* - * @param seq The char sequence to be checked against + * @param seq The char sequence to be checked against, may be {@code null} * @param fromIndex The starting index into the char sequence (inclusive) * @param toIndex The ending index into the char sequence (exclusive) * @throws NullPointerException If the char sequence is null @@ -506,7 +508,7 @@ static void checkFromIndexSize(final int off, final int len, final int arrayLeng * @since 2.21.0 */ public static void checkFromToIndex(final CharSequence seq, int fromIndex, final int toIndex) { - checkFromToIndex(fromIndex, toIndex, requireNonNull(seq, "char sequence").length()); + checkFromToIndex(fromIndex, toIndex, seq != null ? seq.length() : 4); } static void checkFromToIndex(final int fromIndex, final int toIndex, final int length) { diff --git a/src/main/java/org/apache/commons/io/input/UnsynchronizedBufferedReader.java b/src/main/java/org/apache/commons/io/input/UnsynchronizedBufferedReader.java index e8bf3363f05..bde55873f24 100644 --- a/src/main/java/org/apache/commons/io/input/UnsynchronizedBufferedReader.java +++ b/src/main/java/org/apache/commons/io/input/UnsynchronizedBufferedReader.java @@ -271,11 +271,16 @@ public int read() throws IOException { */ @Override public int read(final char[] buffer, int offset, final int length) throws IOException { + /* + * First throw on a closed reader, then check the parameters. + * + * This behavior is not specified in the Javadoc, but is followed by most readers in java.io. + */ + checkOpen(); IOUtils.checkFromIndexSize(buffer, offset, length); if (length == 0) { return 0; } - checkOpen(); int outstanding = length; while (outstanding > 0) { diff --git a/src/main/java/org/apache/commons/io/output/NullWriter.java b/src/main/java/org/apache/commons/io/output/NullWriter.java index b209056d194..9741b819766 100644 --- a/src/main/java/org/apache/commons/io/output/NullWriter.java +++ b/src/main/java/org/apache/commons/io/output/NullWriter.java @@ -112,6 +112,7 @@ public void flush() { */ @Override public void write(final char[] chr) { + write(chr, 0, chr.length); //to /dev/null } @@ -124,6 +125,7 @@ public void write(final char[] chr) { */ @Override public void write(final char[] chr, final int st, final int end) { + IOUtils.checkFromIndexSize(chr, st, end); //to /dev/null } @@ -144,6 +146,7 @@ public void write(final int b) { */ @Override public void write(final String str) { + write(str, 0, str.length()); //to /dev/null } @@ -151,12 +154,13 @@ public void write(final String str) { * Does nothing, like writing to {@code /dev/null}. * * @param str The string to write. - * @param st The start offset. - * @param end The number of characters to write. + * @param off The start offset. + * @param len The number of characters to write. */ @Override - public void write(final String str, final int st, final int end) { - IOUtils.checkFromIndexSize(str, st, end); + public void write(final String str, final int off, final int len) { + IOUtils.checkFromIndexSize(str, off, len); + //to /dev/null } } diff --git a/src/test/java/org/apache/commons/io/input/ClosedInputStreamTest.java b/src/test/java/org/apache/commons/io/input/ClosedInputStreamTest.java index b19364d8a17..2586806b3a5 100644 --- a/src/test/java/org/apache/commons/io/input/ClosedInputStreamTest.java +++ b/src/test/java/org/apache/commons/io/input/ClosedInputStreamTest.java @@ -22,6 +22,7 @@ import java.io.InputStream; +import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; /** @@ -86,7 +87,7 @@ void testReadArray() throws Exception { try (ClosedInputStream cis = new ClosedInputStream()) { assertEquals(EOF, cis.read(new byte[4096])); assertEquals(EOF, cis.read(new byte[1])); - assertEquals(EOF, cis.read(new byte[0])); + assertEquals(0, cis.read(IOUtils.EMPTY_BYTE_ARRAY)); } } @@ -95,7 +96,7 @@ void testReadArrayIndex() throws Exception { try (ClosedInputStream cis = new ClosedInputStream()) { assertEquals(EOF, cis.read(new byte[4096], 0, 1)); assertEquals(EOF, cis.read(new byte[1], 0, 1)); - assertEquals(EOF, cis.read(new byte[0], 0, 0)); + assertEquals(0, cis.read(IOUtils.EMPTY_BYTE_ARRAY, 0, 0)); } } diff --git a/src/test/java/org/apache/commons/io/input/ClosedReaderTest.java b/src/test/java/org/apache/commons/io/input/ClosedReaderTest.java index cb57f9e6f91..891049f9ef5 100644 --- a/src/test/java/org/apache/commons/io/input/ClosedReaderTest.java +++ b/src/test/java/org/apache/commons/io/input/ClosedReaderTest.java @@ -46,7 +46,7 @@ void testReadArray() throws Exception { try (Reader reader = new ClosedReader()) { assertEquals(EOF, reader.read(new char[4096])); assertEquals(EOF, reader.read(new char[1])); - assertEquals(EOF, reader.read(new char[0])); + assertEquals(0, reader.read(new char[0])); } } @@ -55,7 +55,7 @@ void testReadArrayIndex() throws Exception { try (Reader reader = new ClosedReader()) { assertEquals(EOF, reader.read(CharBuffer.wrap(new char[4096]))); assertEquals(EOF, reader.read(CharBuffer.wrap(new char[1]))); - assertEquals(EOF, reader.read(CharBuffer.wrap(new char[0]))); + assertEquals(0, reader.read(CharBuffer.wrap(new char[0]))); } } @@ -64,7 +64,7 @@ void testReadCharBuffer() throws Exception { try (Reader reader = new ClosedReader()) { assertEquals(EOF, reader.read(new char[4096])); assertEquals(EOF, reader.read(new char[1])); - assertEquals(EOF, reader.read(new char[0])); + assertEquals(0, reader.read(new char[0])); } } diff --git a/src/test/java/org/apache/commons/io/input/MarkShieldInputStreamTest.java b/src/test/java/org/apache/commons/io/input/MarkShieldInputStreamTest.java index a1e4d17f457..2566f8b51e6 100644 --- a/src/test/java/org/apache/commons/io/input/MarkShieldInputStreamTest.java +++ b/src/test/java/org/apache/commons/io/input/MarkShieldInputStreamTest.java @@ -152,7 +152,7 @@ void testReadByteArrayIntIntAfterClose(final int len) throws Exception { MarkShieldInputStream msis = new MarkShieldInputStream(in)) { assertEquals(len, in.available()); in.close(); - assertEquals(0, in.read(new byte[0], 0, 1)); + assertThrows(IndexOutOfBoundsException.class, () -> in.read(new byte[0], 0, 1)); assertEquals(0, in.read(new byte[1], 0, 0)); assertThrows(IOException.class, () -> in.read(new byte[2], 0, 1)); } diff --git a/src/test/java/org/apache/commons/io/input/NullInputStreamTest.java b/src/test/java/org/apache/commons/io/input/NullInputStreamTest.java index b79555d8c02..9b59ac90c0e 100644 --- a/src/test/java/org/apache/commons/io/input/NullInputStreamTest.java +++ b/src/test/java/org/apache/commons/io/input/NullInputStreamTest.java @@ -249,7 +249,7 @@ void testReadByteArrayIntIntAfterClose() throws Exception { try (InputStream in = new NullInputStream()) { assertEquals(0, in.available()); in.close(); - assertEquals(0, in.read(new byte[0], 0, 1)); + assertThrows(IndexOutOfBoundsException.class, () -> in.read(new byte[0], 0, 1)); assertEquals(0, in.read(new byte[1], 0, 0)); assertThrows(IOException.class, () -> in.read(new byte[2], 0, 1)); } diff --git a/src/test/java/org/apache/commons/io/input/ProxyReaderTest.java b/src/test/java/org/apache/commons/io/input/ProxyReaderTest.java index f33979ce1b8..681ab90eae0 100644 --- a/src/test/java/org/apache/commons/io/input/ProxyReaderTest.java +++ b/src/test/java/org/apache/commons/io/input/ProxyReaderTest.java @@ -38,6 +38,11 @@ public int read(final char[] chars) throws IOException { return chars == null ? 0 : super.read(chars); } + @Override + public int read(final char[] chars, final int offset, final int length) throws IOException { + return chars == null ? 0 : super.read(chars, offset, length); + } + @Override public int read(final CharBuffer target) throws IOException { return target == null ? 0 : super.read(target); diff --git a/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedReaderTest.java b/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedReaderTest.java index 9c7dbf6fd81..a144bc418d0 100644 --- a/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedReaderTest.java +++ b/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedReaderTest.java @@ -353,35 +353,46 @@ void testRead() throws IOException { * @throws IOException test failure. */ @Test - void testReadArray() throws IOException { + void testReadArray1() throws IOException { final char[] ca = new char[2]; try (UnsynchronizedBufferedReader toRet = new UnsynchronizedBufferedReader(new InputStreamReader(new ByteArrayInputStream(new byte[0])))) { - /* Null buffer should throw NPE even when len == 0 */ + /* Validate parameters, before returning 0 */ assertThrows(NullPointerException.class, () -> toRet.read(null, 1, 0)); + assertThrows(IndexOutOfBoundsException.class, () -> toRet.read(ca, 1, 5)); + /* Read zero bytes should return 0 */ + assertEquals(0, toRet.read(ca, 0, 0)); toRet.close(); - assertThrows(IOException.class, () -> toRet.read(null, 1, 0)); - /* Closed reader should throw IOException reading zero bytes */ - assertThrows(IOException.class, () -> toRet.read(ca, 0, 0)); /* - * Closed reader should throw IOException in preference to index out of bounds + * After close, readers in java.io consistently throw IOException before checking parameters or returning 0. */ - // Read should throw IOException before - // ArrayIndexOutOfBoundException + assertThrows(IOException.class, () -> toRet.read(null, 1, 0)); assertThrows(IOException.class, () -> toRet.read(ca, 1, 5)); + assertThrows(IOException.class, () -> toRet.read(ca, 0, 0)); } + } + + @Test + void testReadArray2() throws IOException { + final char[] ca = new char[2]; // Test to ensure that a drained stream returns 0 at EOF try (UnsynchronizedBufferedReader toRet2 = new UnsynchronizedBufferedReader(new InputStreamReader(new ByteArrayInputStream(new byte[2])))) { assertEquals(2, toRet2.read(ca, 0, 2)); assertEquals(-1, toRet2.read(ca, 0, 2)); assertEquals(0, toRet2.read(ca, 0, 0)); } + } + @Test + void testReadArray3() throws IOException { // Test for method int UnsynchronizedBufferedReader.read(char [], int, int) final char[] buf = new char[testString.length()]; br = new UnsynchronizedBufferedReader(new StringReader(testString)); br.read(buf, 50, 500); assertTrue(new String(buf, 50, 500).equals(testString.substring(0, 500))); + } + @Test + void testReadArray4() throws IOException { try (UnsynchronizedBufferedReader bufin = new UnsynchronizedBufferedReader(new Reader() { int size = 2; int pos; @@ -424,11 +435,18 @@ public boolean ready() throws IOException { final int result = bufin.read(new char[2], 0, 2); assertEquals(result, 1); } + } + + @Test + void testReadArray_HARMONY_831() throws IOException { // regression for HARMONY-831 try (Reader reader = new UnsynchronizedBufferedReader(new PipedReader(), 9)) { assertThrows(IndexOutOfBoundsException.class, () -> reader.read(new char[] {}, 7, 0)); } + } + @Test + void testReadArray_HARMONY_54() throws IOException { // Regression for HARMONY-54 final char[] ch = {}; @SuppressWarnings("resource") @@ -456,13 +474,12 @@ void testReadArrayException() throws IOException { br = new UnsynchronizedBufferedReader(new StringReader(testString)); final char[] nullCharArray = null; final char[] charArray = testString.toCharArray(); - assertThrows(IndexOutOfBoundsException.class, () -> br.read(nullCharArray, -1, -1)); - assertThrows(IndexOutOfBoundsException.class, () -> br.read(nullCharArray, -1, 0)); + assertThrows(NullPointerException.class, () -> br.read(nullCharArray, -1, 0)); assertThrows(NullPointerException.class, () -> br.read(nullCharArray, 0, -1)); - assertThrows(NullPointerException.class, () -> br.read(nullCharArray, 0, 0)); - assertThrows(NullPointerException.class, () -> br.read(nullCharArray, 0, 1)); - assertThrows(IndexOutOfBoundsException.class, () -> br.read(charArray, -1, -1)); + assertThrows(NullPointerException.class, () -> br.read(nullCharArray, 1, 1)); assertThrows(IndexOutOfBoundsException.class, () -> br.read(charArray, -1, 0)); + assertThrows(IndexOutOfBoundsException.class, () -> br.read(charArray, 0, -1)); + assertThrows(IndexOutOfBoundsException.class, () -> br.read(charArray, charArray.length, 1)); br.read(charArray, 0, 0); br.read(charArray, 0, charArray.length); diff --git a/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTest.java b/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTest.java index fa19ba2a41a..9bae1cb5f62 100644 --- a/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTest.java +++ b/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTest.java @@ -177,7 +177,7 @@ void testInvalidWriteOffsetOver(final String baosName, final BAOSFactory> baos @MethodSource("baosFactories") void testInvalidWriteOffsetUnder(final String baosName, final BAOSFactory> baosFactory) throws IOException { try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance()) { - assertThrows(IndexOutOfBoundsException.class, () -> baout.write(null, -1, 0)); + assertThrows(IndexOutOfBoundsException.class, () -> baout.write(IOUtils.EMPTY_BYTE_ARRAY, -1, 0)); } } diff --git a/src/test/java/org/apache/commons/io/output/NullAppendableTest.java b/src/test/java/org/apache/commons/io/output/NullAppendableTest.java index 6caa15023d9..09b1d1bf2ba 100644 --- a/src/test/java/org/apache/commons/io/output/NullAppendableTest.java +++ b/src/test/java/org/apache/commons/io/output/NullAppendableTest.java @@ -16,6 +16,8 @@ */ package org.apache.commons.io.output; +import static org.junit.jupiter.api.Assertions.assertThrows; + import java.io.IOException; import org.junit.jupiter.api.Test; @@ -32,7 +34,10 @@ void testNull() throws IOException { appendable.append("A"); appendable.append("A", 0, 1); appendable.append(null, 0, 1); - appendable.append(null, -1, -1); + // `null` is converted to "null" + assertThrows(IndexOutOfBoundsException.class, () -> appendable.append(null, -1, 0)); + assertThrows(IndexOutOfBoundsException.class, () -> appendable.append(null, 1, 0)); + assertThrows(IndexOutOfBoundsException.class, () -> appendable.append(null, 0, 5)); } } diff --git a/src/test/java/org/apache/commons/io/output/ProxyWriterTest.java b/src/test/java/org/apache/commons/io/output/ProxyWriterTest.java index 9954ca06803..e810d39d726 100644 --- a/src/test/java/org/apache/commons/io/output/ProxyWriterTest.java +++ b/src/test/java/org/apache/commons/io/output/ProxyWriterTest.java @@ -205,8 +205,8 @@ public void write(final String str, final int off, final int len) throws IOExcep @Test void testNullCharArray() throws Exception { try (ProxyWriter proxy = new ProxyWriter(NullWriter.INSTANCE)) { - proxy.write((char[]) null); - proxy.write((char[]) null, 0, 0); + assertThrows(NullPointerException.class, () -> proxy.write((char[]) null)); + assertThrows(NullPointerException.class, () -> proxy.write((char[]) null, 0, 0)); } } @@ -220,8 +220,9 @@ void testNullCharSequence() throws Exception { @Test void testNullString() throws Exception { try (ProxyWriter proxy = new ProxyWriter(NullWriter.INSTANCE)) { - proxy.write((String) null); - proxy.write((String) null, 0, 0); + // Default implementation delegates to write(char[], int, int) + assertThrows(NullPointerException.class, () -> proxy.write((String) null)); + assertThrows(NullPointerException.class, () -> proxy.write((String) null, 0, 0)); } } From cbfb55cc15c3f4344bbe7079026b0214024b11e9 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz"The range is valid if all of the following hold:
*If the range is invalid, this method throws an - * {@link IndexOutOfBoundsException} with a descriptive message.
+ *If the range is invalid, throws {@link IndexOutOfBoundsException} with a descriptive message.
* - * @param array The array to be checked against - * @param off The starting offset into the array - * @param len The number of elements in the range - * @throws NullPointerException If the array is null - * @throws IndexOutOfBoundsException If the range {@code [off, off + len)} is out of bounds + *Typical usage in {@link InputStream#read(byte[], int, int)} and {@link OutputStream#write(byte[], int, int)} implementations:
+ * + *{@code
+ * @Override
+ * public int read(byte[] b, int off, int len) throws IOException {
+ * IOUtils.checkFromIndexSize(b, off, len);
+ * if (len == 0) {
+ * return 0;
+ * }
+ * ensureOpen();
+ * // perform read...
+ * }
+ *
+ * @Override
+ * public void write(byte[] b, int off, int len) throws IOException {
+ * IOUtils.checkFromIndexSize(b, off, len);
+ * if (len == 0) {
+ * return;
+ * }
+ * ensureOpen();
+ * // perform write...
+ * }
+ * }
+ *
+ * @param array the array against which the range is validated
+ * @param off the starting offset into the array (inclusive)
+ * @param len the number of elements to access
+ * @throws NullPointerException if {@code array} is {@code null}
+ * @throws IndexOutOfBoundsException if the range {@code [off, off + len)} is out of bounds for {@code array}
+ * @see InputStream#read(byte[], int, int)
+ * @see OutputStream#write(byte[], int, int)
* @since 2.21.0
*/
public static void checkFromIndexSize(final byte[] array, final int off, final int len) {
@@ -431,7 +456,7 @@ public static void checkFromIndexSize(final byte[] array, final int off, final i
}
/**
- * Checks if the sub-range described by an offset and length is valid for the given array.
+ * Validates that the sub-range {@code [off, off + len)} is within the bounds of the given array.
*
* The range is valid if all of the following hold:
*If the range is invalid, this method throws an - * {@link IndexOutOfBoundsException} with a descriptive message.
+ *If the range is invalid, throws {@link IndexOutOfBoundsException} with a descriptive message.
* - * @param array The array to be checked against - * @param off The starting offset into the array - * @param len The number of elements in the range - * @throws NullPointerException If the array is null - * @throws IndexOutOfBoundsException If the range {@code [off, off + len)} is out of bounds + *Typical usage in {@link Reader#read(char[], int, int)} and {@link Writer#write(char[], int, int)} implementations:
+ * + *{@code
+ * @Override
+ * public int read(char[] cbuf, int off, int len) throws IOException {
+ * ensureOpen();
+ * IOUtils.checkFromIndexSize(cbuf, off, len);
+ * if (len == 0) {
+ * return 0;
+ * }
+ * // perform read...
+ * }
+ *
+ * @Override
+ * public void write(char[] cbuf, int off, int len) throws IOException {
+ * ensureOpen();
+ * IOUtils.checkFromIndexSize(cbuf, off, len);
+ * if (len == 0) {
+ * return;
+ * }
+ * // perform write...
+ * }
+ * }
+ *
+ * @param array the array against which the range is validated
+ * @param off the starting offset into the array (inclusive)
+ * @param len the number of characters to access
+ * @throws NullPointerException if {@code array} is {@code null}
+ * @throws IndexOutOfBoundsException if the range {@code [off, off + len)} is out of bounds for {@code array}
+ * @see Reader#read(char[], int, int)
+ * @see Writer#write(char[], int, int)
* @since 2.21.0
*/
public static void checkFromIndexSize(final char[] array, final int off, final int len) {
@@ -455,23 +505,36 @@ public static void checkFromIndexSize(final char[] array, final int off, final i
}
/**
- * Checks if the sub-range described by an offset and length is valid for the given string.
+ * Validates that the sub-range {@code [off, off + len)} is within the bounds of the given string.
*
* The range is valid if all of the following hold:
*If the range is invalid, this method throws an - * {@link IndexOutOfBoundsException} with a descriptive message.
+ *If the range is invalid, throws {@link IndexOutOfBoundsException} with a descriptive message.
+ * + *Typical usage in {@link Writer#write(String, int, int)} implementations:
* - * @param str The char sequence to be checked against - * @param off The starting offset into the array - * @param len The number of elements in the range - * @throws NullPointerException If the array is null - * @throws IndexOutOfBoundsException If the range {@code [off, off + len)} is out of bounds + *{@code
+ * @Override
+ * public void write(String str, int off, int len) throws IOException {
+ * IOUtils.checkFromIndexSize(str, off, len);
+ * if (len == 0) {
+ * return;
+ * }
+ * // perform write...
+ * }
+ * }
+ *
+ * @param str the string against which the range is validated
+ * @param off the starting offset into the string (inclusive)
+ * @param len the number of characters to write
+ * @throws NullPointerException if {@code str} is {@code null}
+ * @throws IndexOutOfBoundsException if the range {@code [off, off + len)} is out of bounds for {@code str}
+ * @see Writer#write(String, int, int)
* @since 2.21.0
*/
public static void checkFromIndexSize(final String str, final int off, final int len) {
@@ -485,27 +548,38 @@ static void checkFromIndexSize(final int off, final int len, final int arrayLeng
}
/**
- * Checks if the sub-sequence described by fromIndex (inclusive) and toIndex (exclusive) is valid for the given {@link CharSequence}.
+ * Validates that the sub-sequence {@code [fromIndex, toIndex)} is within the bounds of the given {@link CharSequence}.
*
- * The range is valid if all of the following hold:
+ *The sub-sequence is valid if all of the following hold:
*If {@code seq} is null, it is treated as the string "null".
+ *If {@code seq} is {@code null}, it is treated as the literal string {@code "null"} (length {@code 4}).
* - *If the range is invalid, this method throws an {@link IndexOutOfBoundsException} with a descriptive message.
+ *If the range is invalid, throws {@link IndexOutOfBoundsException} with a descriptive message.
+ * + *Typical usage in {@link Appendable#append(CharSequence, int, int)} implementations:
+ * + *{@code
+ * @Override
+ * public Appendable append(CharSequence csq, int start, int end) throws IOException {
+ * IOUtils.checkFromToIndex(csq, start, end);
+ * // perform append...
+ * return this;
+ * }
+ * }
*
- * @param seq The char sequence to be checked against, may be {@code null}
- * @param fromIndex The starting index into the char sequence (inclusive)
- * @param toIndex The ending index into the char sequence (exclusive)
- * @throws NullPointerException If the char sequence is null
- * @throws IndexOutOfBoundsException If the range {@code [fromIndex, toIndex)} is out of bounds
+ * @param seq the character sequence to validate (may be {@code null}, treated as {@code "null"})
+ * @param fromIndex the starting index (inclusive)
+ * @param toIndex the ending index (exclusive)
+ * @throws IndexOutOfBoundsException if the range {@code [fromIndex, toIndex)} is out of bounds for {@code seq}
+ * @see Appendable#append(CharSequence, int, int)
* @since 2.21.0
*/
- public static void checkFromToIndex(final CharSequence seq, int fromIndex, final int toIndex) {
+ public static void checkFromToIndex(final CharSequence seq, final int fromIndex, final int toIndex) {
checkFromToIndex(fromIndex, toIndex, seq != null ? seq.length() : 4);
}
From 2220753dc30034d8bbc8ecadb12c92757264d71f Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz" Typical usage in {@link InputStream#read(byte[], int, int)} and {@link OutputStream#write(byte[], int, int)} implementations:
* - *{@code
- * @Override
+ *
+ * {@code @Override}
* public int read(byte[] b, int off, int len) throws IOException {
* IOUtils.checkFromIndexSize(b, off, len);
* if (len == 0) {
@@ -431,7 +431,7 @@ private static char[] charArray(final int size) {
* // perform read...
* }
*
- * @Override
+ * {@code @Override}
* public void write(byte[] b, int off, int len) throws IOException {
* IOUtils.checkFromIndexSize(b, off, len);
* if (len == 0) {
@@ -440,7 +440,7 @@ private static char[] charArray(final int size) {
* ensureOpen();
* // perform write...
* }
- * }
+ *
*
* @param array the array against which the range is validated
* @param off the starting offset into the array (inclusive)
@@ -469,8 +469,8 @@ public static void checkFromIndexSize(final byte[] array, final int off, final i
*
* Typical usage in {@link Reader#read(char[], int, int)} and {@link Writer#write(char[], int, int)} implementations:
* - *{@code
- * @Override
+ *
+ * {@code @Override}
* public int read(char[] cbuf, int off, int len) throws IOException {
* ensureOpen();
* IOUtils.checkFromIndexSize(cbuf, off, len);
@@ -480,7 +480,7 @@ public static void checkFromIndexSize(final byte[] array, final int off, final i
* // perform read...
* }
*
- * @Override
+ * {@code @Override}
* public void write(char[] cbuf, int off, int len) throws IOException {
* ensureOpen();
* IOUtils.checkFromIndexSize(cbuf, off, len);
@@ -489,7 +489,7 @@ public static void checkFromIndexSize(final byte[] array, final int off, final i
* }
* // perform write...
* }
- * }
+ *
*
* @param array the array against which the range is validated
* @param off the starting offset into the array (inclusive)
@@ -518,8 +518,8 @@ public static void checkFromIndexSize(final char[] array, final int off, final i
*
* Typical usage in {@link Writer#write(String, int, int)} implementations:
* - *{@code
- * @Override
+ *
+ * {@code @Override}
* public void write(String str, int off, int len) throws IOException {
* IOUtils.checkFromIndexSize(str, off, len);
* if (len == 0) {
@@ -527,7 +527,7 @@ public static void checkFromIndexSize(final char[] array, final int off, final i
* }
* // perform write...
* }
- * }
+ *
*
* @param str the string against which the range is validated
* @param off the starting offset into the string (inclusive)
@@ -563,14 +563,14 @@ static void checkFromIndexSize(final int off, final int len, final int arrayLeng
*
* Typical usage in {@link Appendable#append(CharSequence, int, int)} implementations:
* - *{@code
- * @Override
+ *
+ * {@code @Override}
* public Appendable append(CharSequence csq, int start, int end) throws IOException {
* IOUtils.checkFromToIndex(csq, start, end);
* // perform append...
* return this;
* }
- * }
+ *
*
* @param seq the character sequence to validate (may be {@code null}, treated as {@code "null"})
* @param fromIndex the starting index (inclusive)
From 41c2a98b4f8d2214051cc35bacd31664cd1ce2a4 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz" Typical usage in {@link InputStream#read(byte[], int, int)} and {@link OutputStream#write(byte[], int, int)} implementations:
* *
- * {@code @Override}
* public int read(byte[] b, int off, int len) throws IOException {
* IOUtils.checkFromIndexSize(b, off, len);
* if (len == 0) {
@@ -431,7 +430,6 @@ private static char[] charArray(final int size) {
* // perform read...
* }
*
- * {@code @Override}
* public void write(byte[] b, int off, int len) throws IOException {
* IOUtils.checkFromIndexSize(b, off, len);
* if (len == 0) {
@@ -470,7 +468,6 @@ public static void checkFromIndexSize(final byte[] array, final int off, final i
* Typical usage in {@link Reader#read(char[], int, int)} and {@link Writer#write(char[], int, int)} implementations:
*
*
- * {@code @Override}
* public int read(char[] cbuf, int off, int len) throws IOException {
* ensureOpen();
* IOUtils.checkFromIndexSize(cbuf, off, len);
@@ -480,7 +477,6 @@ public static void checkFromIndexSize(final byte[] array, final int off, final i
* // perform read...
* }
*
- * {@code @Override}
* public void write(char[] cbuf, int off, int len) throws IOException {
* ensureOpen();
* IOUtils.checkFromIndexSize(cbuf, off, len);
@@ -519,7 +515,6 @@ public static void checkFromIndexSize(final char[] array, final int off, final i
* Typical usage in {@link Writer#write(String, int, int)} implementations:
*
*
- * {@code @Override}
* public void write(String str, int off, int len) throws IOException {
* IOUtils.checkFromIndexSize(str, off, len);
* if (len == 0) {
@@ -564,7 +559,6 @@ static void checkFromIndexSize(final int off, final int len, final int arrayLeng
* Typical usage in {@link Appendable#append(CharSequence, int, int)} implementations:
*
*
- * {@code @Override}
* public Appendable append(CharSequence csq, int start, int end) throws IOException {
* IOUtils.checkFromToIndex(csq, start, end);
* // perform append...
From 68c1ba463d3d0d48ddf2e0ea0a06c5153d5052e1 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 08:51:01 +0200
Subject: [PATCH 09/25] fix: `ClosedReader` Javadoc and tests
---
.../apache/commons/io/input/ClosedReader.java | 17 ++++++++----
.../commons/io/input/ClosedReaderTest.java | 27 ++++++++++++++-----
2 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/input/ClosedReader.java b/src/main/java/org/apache/commons/io/input/ClosedReader.java
index 72220c6978d..b50721c120d 100644
--- a/src/main/java/org/apache/commons/io/input/ClosedReader.java
+++ b/src/main/java/org/apache/commons/io/input/ClosedReader.java
@@ -62,12 +62,19 @@ public void close() throws IOException {
}
/**
- * Returns -1 to indicate that the stream is closed.
+ * A no-op read method that always indicates end-of-stream.
*
- * @param cbuf ignored
- * @param off ignored
- * @param len ignored
- * @return always -1
+ * Behavior:
+ *
+ * - If {@code len == 0}, returns {@code 0} immediately (no characters are read).
+ * - Otherwise, always returns {@value IOUtils#EOF} to signal that the stream is closed or at end-of-stream.
+ *
+ *
+ * @param cbuf The destination buffer.
+ * @param off The offset at which to start storing characters.
+ * @param len The maximum number of characters to read.
+ * @return {@code 0} if {@code len == 0}; otherwise always {@value IOUtils#EOF}.
+ * @throws IndexOutOfBoundsException If {@code off < 0}, {@code len < 0}, or {@code off + len > cbuf.length}.
*/
@Override
public int read(final char[] cbuf, final int off, final int len) {
diff --git a/src/test/java/org/apache/commons/io/input/ClosedReaderTest.java b/src/test/java/org/apache/commons/io/input/ClosedReaderTest.java
index 891049f9ef5..57b87ddfb09 100644
--- a/src/test/java/org/apache/commons/io/input/ClosedReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/ClosedReaderTest.java
@@ -18,6 +18,7 @@
import static org.apache.commons.io.IOUtils.EOF;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException;
import java.io.Reader;
@@ -47,24 +48,38 @@ void testReadArray() throws Exception {
assertEquals(EOF, reader.read(new char[4096]));
assertEquals(EOF, reader.read(new char[1]));
assertEquals(0, reader.read(new char[0]));
+ assertThrows(NullPointerException.class, () -> reader.read((char[]) null));
}
}
@Test
void testReadArrayIndex() throws Exception {
try (Reader reader = new ClosedReader()) {
- assertEquals(EOF, reader.read(CharBuffer.wrap(new char[4096])));
- assertEquals(EOF, reader.read(CharBuffer.wrap(new char[1])));
- assertEquals(0, reader.read(CharBuffer.wrap(new char[0])));
+ final char[] cbuf = new char[4096];
+ assertEquals(EOF, reader.read(cbuf, 0, 2048));
+ assertEquals(EOF, reader.read(cbuf, 2048, 2048));
+ assertEquals(0, reader.read(cbuf, 4096, 0));
+ assertThrows(IndexOutOfBoundsException.class, () -> reader.read(cbuf, -1, 1));
+ assertThrows(IndexOutOfBoundsException.class, () -> reader.read(cbuf, 0, 4097));
+ assertThrows(IndexOutOfBoundsException.class, () -> reader.read(cbuf, 1, -1));
+
+ assertEquals(EOF, reader.read(new char[1]));
+ assertEquals(0, reader.read(new char[0]));
+ assertThrows(NullPointerException.class, () -> reader.read(null, 0, 0));
}
}
@Test
void testReadCharBuffer() throws Exception {
try (Reader reader = new ClosedReader()) {
- assertEquals(EOF, reader.read(new char[4096]));
- assertEquals(EOF, reader.read(new char[1]));
- assertEquals(0, reader.read(new char[0]));
+ final CharBuffer charBuffer = CharBuffer.wrap(new char[4096]);
+ assertEquals(EOF, reader.read(charBuffer));
+ charBuffer.position(4096);
+ assertEquals(0, reader.read(charBuffer));
+
+ assertEquals(EOF, reader.read(CharBuffer.wrap(new char[1])));
+ assertEquals(0, reader.read(CharBuffer.wrap(new char[0])));
+ assertThrows(NullPointerException.class, () -> reader.read((CharBuffer) null));
}
}
From 48fe1c34398470fb139ed306c48885c91da9f857 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 09:25:54 +0200
Subject: [PATCH 10/25] fix: revert `BrokenReader` and add tests
The tests are improved to ensure that the given exception is thrown **before** parameter validation.
---
.../apache/commons/io/input/BrokenReader.java | 30 ++++++++++++++---
.../commons/io/input/BrokenReaderTest.java | 32 ++++++++++++++++---
2 files changed, 52 insertions(+), 10 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/input/BrokenReader.java b/src/main/java/org/apache/commons/io/input/BrokenReader.java
index 61e9bff47ba..f5d5b3b4e13 100644
--- a/src/main/java/org/apache/commons/io/input/BrokenReader.java
+++ b/src/main/java/org/apache/commons/io/input/BrokenReader.java
@@ -18,9 +18,9 @@
import java.io.IOException;
import java.io.Reader;
+import java.nio.CharBuffer;
import java.util.function.Supplier;
-import org.apache.commons.io.IOUtils;
import org.apache.commons.io.function.Erase;
/**
@@ -115,10 +115,30 @@ public void mark(final int readAheadLimit) throws IOException {
*/
@Override
public int read(final char[] cbuf, final int off, final int len) throws IOException {
- IOUtils.checkFromIndexSize(cbuf, off, len);
- if (len == 0) {
- return 0;
- }
+ throw rethrow();
+ }
+
+ /**
+ * Throws the configured exception.
+ *
+ * @param target ignored.
+ * @return nothing.
+ * @throws IOException always throws the exception configured in a constructor.
+ */
+ @Override
+ public int read(CharBuffer target) throws IOException {
+ throw rethrow();
+ }
+
+ /**
+ * Throws the configured exception.
+ *
+ * @param cbuf ignored.
+ * @return nothing.
+ * @throws IOException always throws the exception configured in a constructor.
+ */
+ @Override
+ public int read(char[] cbuf) throws IOException {
throw rethrow();
}
diff --git a/src/test/java/org/apache/commons/io/input/BrokenReaderTest.java b/src/test/java/org/apache/commons/io/input/BrokenReaderTest.java
index aca91077e6b..02c535a9ec3 100644
--- a/src/test/java/org/apache/commons/io/input/BrokenReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/BrokenReaderTest.java
@@ -22,6 +22,7 @@
import java.io.IOException;
import java.io.Reader;
+import java.nio.CharBuffer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
@@ -45,7 +46,7 @@ void testClose(final Class clazz) throws Exception {
final Throwable exception = clazz.newInstance();
@SuppressWarnings("resource")
final BrokenReader brokenReader = createBrokenReader(exception);
- assertEquals(exception, assertThrows(clazz, () -> brokenReader.close()));
+ assertEquals(exception, assertThrows(clazz, brokenReader::close));
}
@Test
@@ -68,7 +69,7 @@ void testRead(final Class clazz) throws Exception {
final Throwable exception = clazz.newInstance();
@SuppressWarnings("resource")
final BrokenReader brokenReader = createBrokenReader(exception);
- assertEquals(exception, assertThrows(clazz, () -> brokenReader.read()));
+ assertEquals(exception, assertThrows(clazz, brokenReader::read));
}
@ParameterizedTest
@@ -78,6 +79,8 @@ void testReadCharArray(final Class clazz) throws Exception {
@SuppressWarnings("resource")
final BrokenReader brokenReader = createBrokenReader(exception);
assertEquals(exception, assertThrows(clazz, () -> brokenReader.read(new char[1])));
+ // Also throws the exception before checking arguments
+ assertEquals(exception, assertThrows(clazz, () -> brokenReader.read((char[]) null)));
}
@ParameterizedTest
@@ -86,7 +89,26 @@ void testReadCharArrayIndexed(final Class clazz) throws Exception {
final Throwable exception = clazz.newInstance();
@SuppressWarnings("resource")
final BrokenReader brokenReader = createBrokenReader(exception);
- assertEquals(exception, assertThrows(clazz, () -> brokenReader.read(new char[1], 0, 1)));
+ final char[] cbuf = new char[1];
+ assertEquals(exception, assertThrows(clazz, () -> brokenReader.read(cbuf, 0, 1)));
+ // Also throws the exception before checking arguments
+ assertEquals(exception, assertThrows(clazz, () -> brokenReader.read(cbuf, -1, 1)));
+ assertEquals(exception, assertThrows(clazz, () -> brokenReader.read(cbuf, 0, -1)));
+ assertEquals(exception, assertThrows(clazz, () -> brokenReader.read(cbuf, 1, 1)));
+ assertEquals(exception, assertThrows(clazz, () -> brokenReader.read(null, 0, 0)));
+
+ }
+
+ @ParameterizedTest
+ @MethodSource("org.apache.commons.io.BrokenTestFactories#parameters")
+ void testReadCharBuffer(final Class clazz) throws Exception {
+ final Throwable exception = clazz.newInstance();
+ @SuppressWarnings("resource")
+ final BrokenReader brokenReader = createBrokenReader(exception);
+ final CharBuffer charBuffer = CharBuffer.allocate(1);
+ assertEquals(exception, assertThrows(clazz, () -> brokenReader.read(charBuffer)));
+ // Also throws the exception before checking arguments
+ assertEquals(exception, assertThrows(clazz, () -> brokenReader.read((CharBuffer) null)));
}
@ParameterizedTest
@@ -95,7 +117,7 @@ void testReady(final Class clazz) throws Exception {
final Throwable exception = clazz.newInstance();
@SuppressWarnings("resource")
final BrokenReader brokenReader = createBrokenReader(exception);
- assertEquals(exception, assertThrows(clazz, () -> brokenReader.ready()));
+ assertEquals(exception, assertThrows(clazz, brokenReader::ready));
}
@ParameterizedTest
@@ -104,7 +126,7 @@ void testReset(final Class clazz) throws Exception {
final Throwable exception = clazz.newInstance();
@SuppressWarnings("resource")
final BrokenReader brokenReader = createBrokenReader(exception);
- assertEquals(exception, assertThrows(clazz, () -> brokenReader.reset()));
+ assertEquals(exception, assertThrows(clazz, brokenReader::reset));
}
@ParameterizedTest
From bbde62705159668acddaaab29163346e06d05119 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 10:05:32 +0200
Subject: [PATCH 11/25] fix: remove `BrokenReader.read(char[]/CharBuffer)`
overrides
---
.../apache/commons/io/input/BrokenReader.java | 24 -------------------
.../commons/io/input/BrokenReaderTest.java | 4 ----
2 files changed, 28 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/input/BrokenReader.java b/src/main/java/org/apache/commons/io/input/BrokenReader.java
index f5d5b3b4e13..2467228b9a9 100644
--- a/src/main/java/org/apache/commons/io/input/BrokenReader.java
+++ b/src/main/java/org/apache/commons/io/input/BrokenReader.java
@@ -118,30 +118,6 @@ public int read(final char[] cbuf, final int off, final int len) throws IOExcept
throw rethrow();
}
- /**
- * Throws the configured exception.
- *
- * @param target ignored.
- * @return nothing.
- * @throws IOException always throws the exception configured in a constructor.
- */
- @Override
- public int read(CharBuffer target) throws IOException {
- throw rethrow();
- }
-
- /**
- * Throws the configured exception.
- *
- * @param cbuf ignored.
- * @return nothing.
- * @throws IOException always throws the exception configured in a constructor.
- */
- @Override
- public int read(char[] cbuf) throws IOException {
- throw rethrow();
- }
-
/**
* Throws the configured exception.
*
diff --git a/src/test/java/org/apache/commons/io/input/BrokenReaderTest.java b/src/test/java/org/apache/commons/io/input/BrokenReaderTest.java
index 02c535a9ec3..a47687abf3d 100644
--- a/src/test/java/org/apache/commons/io/input/BrokenReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/BrokenReaderTest.java
@@ -79,8 +79,6 @@ void testReadCharArray(final Class clazz) throws Exception {
@SuppressWarnings("resource")
final BrokenReader brokenReader = createBrokenReader(exception);
assertEquals(exception, assertThrows(clazz, () -> brokenReader.read(new char[1])));
- // Also throws the exception before checking arguments
- assertEquals(exception, assertThrows(clazz, () -> brokenReader.read((char[]) null)));
}
@ParameterizedTest
@@ -107,8 +105,6 @@ void testReadCharBuffer(final Class clazz) throws Exception {
final BrokenReader brokenReader = createBrokenReader(exception);
final CharBuffer charBuffer = CharBuffer.allocate(1);
assertEquals(exception, assertThrows(clazz, () -> brokenReader.read(charBuffer)));
- // Also throws the exception before checking arguments
- assertEquals(exception, assertThrows(clazz, () -> brokenReader.read((CharBuffer) null)));
}
@ParameterizedTest
From de2d1e376421622ea3f700e6c59ff90a8e2c143d Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 10:13:51 +0200
Subject: [PATCH 12/25] fix: revert `BrokenWriter`
Revert `BrokenWriter` and add test cases to ensure the exception is thrown **before** parameter validation.
---
.../org/apache/commons/io/output/BrokenWriter.java | 2 --
.../apache/commons/io/output/BrokenWriterTest.java | 14 +++++++++++---
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/output/BrokenWriter.java b/src/main/java/org/apache/commons/io/output/BrokenWriter.java
index 1feac6bb84a..7fe244d2392 100644
--- a/src/main/java/org/apache/commons/io/output/BrokenWriter.java
+++ b/src/main/java/org/apache/commons/io/output/BrokenWriter.java
@@ -20,7 +20,6 @@
import java.io.Writer;
import java.util.function.Supplier;
-import org.apache.commons.io.IOUtils;
import org.apache.commons.io.function.Erase;
/**
@@ -122,7 +121,6 @@ private RuntimeException rethrow() {
*/
@Override
public void write(final char[] cbuf, final int off, final int len) throws IOException {
- IOUtils.checkFromIndexSize(cbuf, off, len);
throw rethrow();
}
diff --git a/src/test/java/org/apache/commons/io/output/BrokenWriterTest.java b/src/test/java/org/apache/commons/io/output/BrokenWriterTest.java
index f8db50a6729..dffeb7907ae 100644
--- a/src/test/java/org/apache/commons/io/output/BrokenWriterTest.java
+++ b/src/test/java/org/apache/commons/io/output/BrokenWriterTest.java
@@ -55,6 +55,7 @@ void testAppendCharSequence(final Class clazz) throws Exception {
@SuppressWarnings("resource")
final BrokenWriter brokenWriter = createBrokenWriter(exception);
assertEquals(exception, assertThrows(clazz, () -> brokenWriter.append("01")));
+ assertEquals(exception, assertThrows(clazz, () -> brokenWriter.append(null)));
}
@ParameterizedTest
@@ -64,6 +65,7 @@ void testAppendCharSequenceIndexed(final Class clazz) throws Exceptio
@SuppressWarnings("resource")
final BrokenWriter brokenWriter = createBrokenWriter(exception);
assertEquals(exception, assertThrows(clazz, () -> brokenWriter.append("01", 0, 1)));
+ assertEquals(exception, assertThrows(clazz, () -> brokenWriter.append(null, 0, 4)));
}
@ParameterizedTest
@@ -72,7 +74,7 @@ void testClose(final Class clazz) throws Exception {
final Throwable exception = clazz.newInstance();
@SuppressWarnings("resource")
final BrokenWriter brokenWriter = createBrokenWriter(exception);
- assertEquals(exception, assertThrows(clazz, () -> brokenWriter.close()));
+ assertEquals(exception, assertThrows(clazz, brokenWriter::close));
}
@ParameterizedTest
@@ -81,7 +83,7 @@ void testFlush(final Class clazz) throws Exception {
final Throwable exception = clazz.newInstance();
@SuppressWarnings("resource")
final BrokenWriter brokenWriter = createBrokenWriter(exception);
- assertEquals(exception, assertThrows(clazz, () -> brokenWriter.flush()));
+ assertEquals(exception, assertThrows(clazz, brokenWriter::flush));
}
@Test
@@ -119,7 +121,13 @@ void testWriteCharArrayIndexed(final Class clazz) throws Exception {
final Throwable exception = clazz.newInstance();
@SuppressWarnings("resource")
final BrokenWriter brokenWriter = createBrokenWriter(exception);
- assertEquals(exception, assertThrows(clazz, () -> brokenWriter.write(new char[1], 0, 1)));
+ final char[] cbuf = new char[1];
+ assertEquals(exception, assertThrows(clazz, () -> brokenWriter.write(cbuf, 0, 1)));
+ // Verify that the exception is thrown before checking the parameters.
+ assertEquals(exception, assertThrows(clazz, () -> brokenWriter.write(cbuf, -1, 0)));
+ assertEquals(exception, assertThrows(clazz, () -> brokenWriter.write(cbuf, 0, -1)));
+ assertEquals(exception, assertThrows(clazz, () -> brokenWriter.write(cbuf, 0, 2)));
+ assertEquals(exception, assertThrows(clazz, () -> brokenWriter.write((char[]) null, 0, 0)));
}
@ParameterizedTest
From b0bea24988abd922d640ffacc20abe3b5201cc42 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 10:14:59 +0200
Subject: [PATCH 13/25] fix: Checkstyle failures
---
src/main/java/org/apache/commons/io/input/BrokenReader.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/main/java/org/apache/commons/io/input/BrokenReader.java b/src/main/java/org/apache/commons/io/input/BrokenReader.java
index 2467228b9a9..8fb510569cf 100644
--- a/src/main/java/org/apache/commons/io/input/BrokenReader.java
+++ b/src/main/java/org/apache/commons/io/input/BrokenReader.java
@@ -18,7 +18,6 @@
import java.io.IOException;
import java.io.Reader;
-import java.nio.CharBuffer;
import java.util.function.Supplier;
import org.apache.commons.io.function.Erase;
From 06cb03b158c105427f79c1c39b7a50a5bb57ba8d Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 10:33:13 +0200
Subject: [PATCH 14/25] fix: revert `ClosedWriter` and add tests
---
.../java/org/apache/commons/io/output/ClosedWriter.java | 6 ++----
.../org/apache/commons/io/output/ClosedWriterTest.java | 8 +++++++-
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/output/ClosedWriter.java b/src/main/java/org/apache/commons/io/output/ClosedWriter.java
index bc1add898ad..c4e1d93b93c 100644
--- a/src/main/java/org/apache/commons/io/output/ClosedWriter.java
+++ b/src/main/java/org/apache/commons/io/output/ClosedWriter.java
@@ -19,8 +19,7 @@
import java.io.IOException;
import java.io.Writer;
-
-import org.apache.commons.io.IOUtils;
+import java.util.Arrays;
/**
* Throws an IOException on all attempts to write with {@link #close()} implemented as a noop.
@@ -79,7 +78,6 @@ public void flush() throws IOException {
*/
@Override
public void write(final char[] cbuf, final int off, final int len) throws IOException {
- IOUtils.checkFromIndexSize(cbuf, off, len);
- throw new IOException("write(" + new String(cbuf) + ", " + off + ", " + len + ") failed: stream is closed");
+ throw new IOException(String.format("write(%s, %d, %d) failed: stream is closed", Arrays.toString(cbuf), off, len));
}
}
diff --git a/src/test/java/org/apache/commons/io/output/ClosedWriterTest.java b/src/test/java/org/apache/commons/io/output/ClosedWriterTest.java
index 4f57957a885..b9758729f1b 100644
--- a/src/test/java/org/apache/commons/io/output/ClosedWriterTest.java
+++ b/src/test/java/org/apache/commons/io/output/ClosedWriterTest.java
@@ -43,7 +43,13 @@ void testFlush() throws IOException {
@Test
void testWrite() throws IOException {
try (ClosedWriter cw = new ClosedWriter()) {
- assertThrows(IOException.class, () -> cw.write(new char[0], 0, 0));
+ final char[] cbuf = new char[1];
+ assertThrows(IOException.class, () -> cw.write(new char[0], 0, 1));
+ // In writers, testing for closed always comes before argument validation
+ assertThrows(IOException.class, () -> cw.write(cbuf, -1, 0));
+ assertThrows(IOException.class, () -> cw.write(cbuf, 0, -1));
+ assertThrows(IOException.class, () -> cw.write(cbuf, 0, 2));
+ assertThrows(IOException.class, () -> cw.write((char[]) null, 0, 0));
}
}
From 3678d1e22600febb067c606171ab77ff26c77241 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 10:50:19 +0200
Subject: [PATCH 15/25] fix: revert `FilterCollectionWriter` changes
---
.../org/apache/commons/io/output/FilterCollectionWriter.java | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java b/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java
index 1a13d90eff3..d78f049e635 100644
--- a/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java
+++ b/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java
@@ -28,7 +28,6 @@
import org.apache.commons.io.IOExceptionList;
import org.apache.commons.io.IOIndexedException;
-import org.apache.commons.io.IOUtils;
import org.apache.commons.io.function.IOConsumer;
/**
@@ -129,7 +128,6 @@ public void write(final char[] cbuf) throws IOException {
@SuppressWarnings("resource") // no allocation
@Override
public void write(final char[] cbuf, final int off, final int len) throws IOException {
- IOUtils.checkFromIndexSize(cbuf, off, len);
forAllWriters(w -> w.write(cbuf, off, len));
}
@@ -161,7 +159,6 @@ public void write(final String str) throws IOException {
@SuppressWarnings("resource") // no allocation
@Override
public void write(final String str, final int off, final int len) throws IOException {
- IOUtils.checkFromIndexSize(str, off, len);
forAllWriters(w -> w.write(str, off, len));
}
From d00d5effe0da276f7c0d0caa1b1cef77f6c2103d Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 11:03:24 +0200
Subject: [PATCH 16/25] fix: fix `NullAppendable` Javadoc
---
.../apache/commons/io/output/NullAppendable.java | 15 +++++++++++++++
.../commons/io/output/NullAppendableTest.java | 10 ++++++----
2 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/output/NullAppendable.java b/src/main/java/org/apache/commons/io/output/NullAppendable.java
index fee5dace602..7adbc2d3749 100644
--- a/src/main/java/org/apache/commons/io/output/NullAppendable.java
+++ b/src/main/java/org/apache/commons/io/output/NullAppendable.java
@@ -51,6 +51,21 @@ public Appendable append(final CharSequence csq) throws IOException {
return this;
}
+ /**
+ * Appends a subsequence of the specified character sequence to this Appendable.
+ *
+ * @param csq The character sequence from which a subsequence will be
+ * appended.
+ * If {@code csq} is {@code null}, it is treated as if it were
+ * {@code "null"}.
+ * @param start The index of the first character in the subsequence.
+ * @param end The index of the character following the last character in the
+ * subsequence.
+ * @return {@code this} instance.
+ * @throws IndexOutOfBoundsException if {@code start} or {@code end} are negative, {@code end} is
+ * greater than {@code csq.length()}, or {@code start} is greater
+ * than {@code end}.
+ */
@Override
public Appendable append(final CharSequence csq, final int start, final int end) throws IOException {
IOUtils.checkFromToIndex(csq, start, end);
diff --git a/src/test/java/org/apache/commons/io/output/NullAppendableTest.java b/src/test/java/org/apache/commons/io/output/NullAppendableTest.java
index 09b1d1bf2ba..aa5ef7137ed 100644
--- a/src/test/java/org/apache/commons/io/output/NullAppendableTest.java
+++ b/src/test/java/org/apache/commons/io/output/NullAppendableTest.java
@@ -34,10 +34,12 @@ void testNull() throws IOException {
appendable.append("A");
appendable.append("A", 0, 1);
appendable.append(null, 0, 1);
- // `null` is converted to "null"
- assertThrows(IndexOutOfBoundsException.class, () -> appendable.append(null, -1, 0));
- assertThrows(IndexOutOfBoundsException.class, () -> appendable.append(null, 1, 0));
- assertThrows(IndexOutOfBoundsException.class, () -> appendable.append(null, 0, 5));
+ // Check argument validation
+ final CharSequence csq = "ABCDE";
+ assertThrows(IndexOutOfBoundsException.class, () -> appendable.append(csq, -1, 0));
+ assertThrows(IndexOutOfBoundsException.class, () -> appendable.append(csq, 0, -1));
+ assertThrows(IndexOutOfBoundsException.class, () -> appendable.append(csq, 1, 0));
+ assertThrows(IndexOutOfBoundsException.class, () -> appendable.append(csq, 0, 6));
}
}
From 1e558f5e05cb2c67fc3a4f7404a73ddae02c6a81 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 11:09:22 +0200
Subject: [PATCH 17/25] fix: fix `NullOutputStream` Javadoc and enhance tests
---
.../apache/commons/io/output/NullOutputStream.java | 13 +++++++++----
.../commons/io/output/NullOutputStreamTest.java | 11 ++++++++++-
2 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/output/NullOutputStream.java b/src/main/java/org/apache/commons/io/output/NullOutputStream.java
index 682d0f8e768..0334a4ea4b8 100644
--- a/src/main/java/org/apache/commons/io/output/NullOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/NullOutputStream.java
@@ -69,11 +69,16 @@ public void write(final byte[] b) throws IOException {
}
/**
- * Does nothing.
+ * No-op operation.
*
- * @param b This method ignores this parameter.
- * @param off This method ignores this parameter.
- * @param len This method ignores this parameter.
+ * Validates the arguments but does not write the data.
+ *
+ * @param b The byte array to write from, not {@code null}.
+ * @param off The offset to start at.
+ * @param len The number of bytes to write.
+ * @throws NullPointerException If {@code b} is {@code null}.
+ * @throws IndexOutOfBoundsException If {@code off} or {@code len} are negative, {@code off + len} is greater than
+ * {@code b.length}.
*/
@Override
public void write(final byte[] b, final int off, final int len) {
diff --git a/src/test/java/org/apache/commons/io/output/NullOutputStreamTest.java b/src/test/java/org/apache/commons/io/output/NullOutputStreamTest.java
index bdecc0e0a68..097566fc6e2 100644
--- a/src/test/java/org/apache/commons/io/output/NullOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/output/NullOutputStreamTest.java
@@ -16,6 +16,8 @@
*/
package org.apache.commons.io.output;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
import java.io.IOException;
import org.junit.jupiter.api.Test;
@@ -34,11 +36,18 @@ private void process(final NullOutputStream nos) throws IOException {
nos.close();
nos.write("allowed".getBytes());
nos.write(255);
+ // Test arguments validation
+ final byte[] b = new byte[1];
+ assertThrows(IndexOutOfBoundsException.class, () -> nos.write(b, -1, 0));
+ assertThrows(IndexOutOfBoundsException.class, () -> nos.write(b, 0, -1));
+ assertThrows(IndexOutOfBoundsException.class, () -> nos.write(b, 0, 2));
+ assertThrows(NullPointerException.class, () -> nos.write(null, 0, 0));
}
@Test
+ @SuppressWarnings("deprecation")
void testNewInstance() throws IOException {
- try (NullOutputStream nos = NullOutputStream.INSTANCE) {
+ try (NullOutputStream nos = new NullOutputStream()) {
process(nos);
}
}
From 6585383d23a7cc02acfb977125b16e00965d5458 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 11:37:35 +0200
Subject: [PATCH 18/25] fix: fix `NullWriter` Javadoc and enhance tests
---
.../commons/io/output/NullAppendable.java | 4 +-
.../apache/commons/io/output/NullWriter.java | 47 +++++----
.../commons/io/output/NullWriterTest.java | 96 +++++++++++++++++--
3 files changed, 121 insertions(+), 26 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/output/NullAppendable.java b/src/main/java/org/apache/commons/io/output/NullAppendable.java
index 7adbc2d3749..c5ea2cfcbeb 100644
--- a/src/main/java/org/apache/commons/io/output/NullAppendable.java
+++ b/src/main/java/org/apache/commons/io/output/NullAppendable.java
@@ -52,7 +52,7 @@ public Appendable append(final CharSequence csq) throws IOException {
}
/**
- * Appends a subsequence of the specified character sequence to this Appendable.
+ * Does nothing except argument validation, like writing to {@code /dev/null}.
*
* @param csq The character sequence from which a subsequence will be
* appended.
@@ -62,7 +62,7 @@ public Appendable append(final CharSequence csq) throws IOException {
* @param end The index of the character following the last character in the
* subsequence.
* @return {@code this} instance.
- * @throws IndexOutOfBoundsException if {@code start} or {@code end} are negative, {@code end} is
+ * @throws IndexOutOfBoundsException If {@code start} or {@code end} are negative, {@code end} is
* greater than {@code csq.length()}, or {@code start} is greater
* than {@code end}.
*/
diff --git a/src/main/java/org/apache/commons/io/output/NullWriter.java b/src/main/java/org/apache/commons/io/output/NullWriter.java
index 9741b819766..0ef9a09cdbf 100644
--- a/src/main/java/org/apache/commons/io/output/NullWriter.java
+++ b/src/main/java/org/apache/commons/io/output/NullWriter.java
@@ -79,12 +79,19 @@ public Writer append(final CharSequence csq) {
}
/**
- * Does nothing, like writing to {@code /dev/null}.
+ * Does nothing except argument validation, like writing to {@code /dev/null}.
*
- * @param csq The character sequence to write.
- * @param start The index of the first character to write.
- * @param end The index of the first character to write (exclusive).
- * @return this writer.
+ * @param csq The character sequence from which a subsequence will be
+ * appended.
+ * If {@code csq} is {@code null}, it is treated as if it were
+ * {@code "null"}.
+ * @param start The index of the first character in the subsequence.
+ * @param end The index of the character following the last character in the
+ * subsequence.
+ * @return {@code this} instance.
+ * @throws IndexOutOfBoundsException If {@code start} or {@code end} are negative, {@code end} is
+ * greater than {@code csq.length()}, or {@code start} is greater
+ * than {@code end}.
* @since 2.0
*/
@Override
@@ -106,9 +113,10 @@ public void flush() {
}
/**
- * Does nothing, like writing to {@code /dev/null}.
+ * Does nothing except argument validation, like writing to {@code /dev/null}.
*
- * @param chr The characters to write
+ * @param chr The characters to write, not {@code null}.
+ * @throws NullPointerException if {@code chr} is {@code null}.
*/
@Override
public void write(final char[] chr) {
@@ -117,15 +125,17 @@ public void write(final char[] chr) {
}
/**
- * Does nothing, like writing to {@code /dev/null}.
+ * Does nothing except argument validation, like writing to {@code /dev/null}.
*
- * @param chr The characters to write.
- * @param st The start offset.
- * @param end The number of characters to write.
+ * @param cbuf The characters to write, not {@code null}.
+ * @param off The start offset.
+ * @param len The number of characters to write.
+ * @throws NullPointerException if {@code chr} is {@code null}.
+ * @throws IndexOutOfBoundsException If ({@code off} or {@code len} are negative, or {@code off + len} is greater than {@code cbuf.length}.
*/
@Override
- public void write(final char[] chr, final int st, final int end) {
- IOUtils.checkFromIndexSize(chr, st, end);
+ public void write(final char[] cbuf, final int off, final int len) {
+ IOUtils.checkFromIndexSize(cbuf, off, len);
//to /dev/null
}
@@ -140,9 +150,10 @@ public void write(final int b) {
}
/**
- * Does nothing, like writing to {@code /dev/null}.
+ * Does nothing except argument validation, like writing to {@code /dev/null}.
*
- * @param str The string to write.
+ * @param str The string to write, not {@code null}.
+ * @throws NullPointerException if {@code str} is {@code null}.
*/
@Override
public void write(final String str) {
@@ -151,11 +162,13 @@ public void write(final String str) {
}
/**
- * Does nothing, like writing to {@code /dev/null}.
+ * Does nothing except argument validation, like writing to {@code /dev/null}.
*
- * @param str The string to write.
+ * @param str The string to write, not {@code null}.
* @param off The start offset.
* @param len The number of characters to write.
+ * @throws NullPointerException If {@code str} is {@code null}.
+ * @throws IndexOutOfBoundsException If ({@code off} or {@code len} are negative, or {@code off + len} is greater than {@code str.length()}.
*/
@Override
public void write(final String str, final int off, final int len) {
diff --git a/src/test/java/org/apache/commons/io/output/NullWriterTest.java b/src/test/java/org/apache/commons/io/output/NullWriterTest.java
index f1b603a3776..424d076ee21 100644
--- a/src/test/java/org/apache/commons/io/output/NullWriterTest.java
+++ b/src/test/java/org/apache/commons/io/output/NullWriterTest.java
@@ -16,6 +16,9 @@
*/
package org.apache.commons.io.output;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
import org.junit.jupiter.api.Test;
/**
@@ -24,17 +27,96 @@
*/
class NullWriterTest {
+ private static final String TEST_STRING = "ABC";
+ private static final char[] TEST_CHARS = TEST_STRING.toCharArray();
+
+ @Test
+ void testAppendChar() {
+ try (NullWriter writer = NullWriter.INSTANCE) {
+ assertSame(writer, writer.append('X'));
+ }
+ }
+
+ @Test
+ void testAppendCharSequence() {
+ try (NullWriter writer = NullWriter.INSTANCE) {
+ assertSame(writer, writer.append(TEST_STRING));
+ assertSame(writer, writer.append(null));
+ }
+ }
+
@Test
- void testNull() {
- final char[] chars = { 'A', 'B', 'C' };
+ void testAppendCharSequenceWithRange() {
+ try (NullWriter writer = NullWriter.INSTANCE) {
+ assertSame(writer, writer.append(TEST_STRING, 1, 2));
+ assertSame(writer, writer.append(null, 0, 4));
+ // Test argument validation
+ assertThrows(IndexOutOfBoundsException.class, () -> writer.append(TEST_STRING, -1, 2));
+ assertThrows(IndexOutOfBoundsException.class, () -> writer.append(TEST_STRING, 1, 5));
+ assertThrows(IndexOutOfBoundsException.class, () -> writer.append(TEST_STRING, 2, 1));
+ }
+ }
+
+ @Test
+ void testCloseNoOp() {
+ final NullWriter writer = NullWriter.INSTANCE;
+ writer.close();
+ writer.write(TEST_CHARS);
+ }
+
+ @Test
+ void testFlush() {
try (NullWriter writer = NullWriter.INSTANCE) {
- writer.write(1);
- writer.write(chars);
- writer.write(chars, 1, 1);
- writer.write("some string");
- writer.write("some string", 2, 2);
writer.flush();
}
}
+ @Test
+ void testWriteCharArray() {
+ try (NullWriter writer = NullWriter.INSTANCE) {
+ writer.write(TEST_CHARS);
+ // Test argument validation
+ assertThrows(NullPointerException.class, () -> writer.write((char[]) null));
+ }
+ }
+
+ @Test
+ void testWriteCharArrayWithOffset() {
+ try (NullWriter writer = NullWriter.INSTANCE) {
+ writer.write(TEST_CHARS, 1, 2);
+ // Test argument validation
+ assertThrows(IndexOutOfBoundsException.class, () -> writer.write(TEST_CHARS, -1, 0));
+ assertThrows(IndexOutOfBoundsException.class, () -> writer.write(TEST_CHARS, 0, -1));
+ assertThrows(IndexOutOfBoundsException.class, () -> writer.write(TEST_CHARS, 0, 4));
+ assertThrows(NullPointerException.class, () -> writer.write((char[]) null, 0, 0));
+ }
+ }
+
+ @Test
+ void testWriteInt() {
+ try (NullWriter writer = NullWriter.INSTANCE) {
+ writer.write(42);
+ }
+ }
+
+ @Test
+ void testWriteString() {
+ try (NullWriter writer = NullWriter.INSTANCE) {
+ writer.write(TEST_STRING);
+ // Test argument validation
+ assertThrows(NullPointerException.class, () -> writer.write((String) null));
+ }
+ }
+
+ @Test
+ void testWriteStringWithOffset() {
+ try (NullWriter writer = NullWriter.INSTANCE) {
+ writer.write(TEST_STRING, 1, 1);
+ // Test argument validation
+ assertThrows(IndexOutOfBoundsException.class, () -> writer.write(TEST_STRING, -1, 0));
+ assertThrows(IndexOutOfBoundsException.class, () -> writer.write(TEST_STRING, 0, -1));
+ assertThrows(IndexOutOfBoundsException.class, () -> writer.write(TEST_STRING, 0, 4));
+ assertThrows(NullPointerException.class, () -> writer.write((String) null, 0, 0));
+ }
+ }
}
From 14a250629ba13693c802a5a47592b1c634e1fb2e Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 22:19:11 +0200
Subject: [PATCH 19/25] fix: `NullInputStream` Javadoc
---
.../org/apache/commons/io/input/NullInputStream.java | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/input/NullInputStream.java b/src/main/java/org/apache/commons/io/input/NullInputStream.java
index 32b1206ff2f..5fa9b817b5c 100644
--- a/src/main/java/org/apache/commons/io/input/NullInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/NullInputStream.java
@@ -243,9 +243,9 @@ protected int processByte() {
* This implementation leaves the byte array unchanged.
*
*
- * @param bytes The byte array
- * @param offset The offset to start at.
- * @param length The number of bytes.
+ * @param bytes The byte array, never {@code null}.
+ * @param offset The offset to start at, always non-negative.
+ * @param length The number of bytes to process, always non-negative and at most {@code bytes.length - offset}.
*/
protected void processBytes(final byte[] bytes, final int offset, final int length) {
// do nothing - overridable by subclass
@@ -274,6 +274,7 @@ public int read() throws IOException {
*
* @param bytes The byte array to read into
* @return The number of bytes read or {@code -1} if the end of file has been reached and {@code throwEofException} is set to {@code false}.
+ * @throws NullPointerException if the byte array is {@code null}.
* @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
* @throws IOException if trying to read past the end of file.
*/
@@ -289,6 +290,8 @@ public int read(final byte[] bytes) throws IOException {
* @param offset The offset to start reading bytes into.
* @param length The number of bytes to read.
* @return The number of bytes read or {@code -1} if the end of file has been reached and {@code throwEofException} is set to {@code false}.
+ * @throws NullPointerException if the byte array is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code offset} or {@code length} are negative, or if {@code offset + length} is greater than {@code bytes.length}.
* @throws EOFException if the end of file is reached and {@code throwEofException} is set to {@code true}.
* @throws IOException if trying to read past the end of file.
*/
From 1632fb2edcd4dd3f425b0e84e1111f0cfa251fe9 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 22:22:42 +0200
Subject: [PATCH 20/25] fix: `ClosedOutputStream` Javadoc
---
.../org/apache/commons/io/output/ClosedOutputStream.java | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/output/ClosedOutputStream.java b/src/main/java/org/apache/commons/io/output/ClosedOutputStream.java
index faa342f40f6..c7216ce6f90 100644
--- a/src/main/java/org/apache/commons/io/output/ClosedOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ClosedOutputStream.java
@@ -68,9 +68,11 @@ public void flush() throws IOException {
/**
* Throws an {@link IOException} to indicate that the stream is closed.
*
- * @param b ignored.
- * @param off ignored.
- * @param len ignored.
+ * @param b Byte array, never {@code null}.
+ * @param off The start offset in the byte array.
+ * @param len The number of bytes to write.
+ * @throws NullPointerException if the byte array is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code off} or {@code len} are negative, or if {@code off + len} is greater than {@code b.length}.
* @throws IOException always thrown.
*/
@Override
From 94760281b40b4a442c7d784713525e94bb0ed694 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 22:26:12 +0200
Subject: [PATCH 21/25] fix: `StringBuilderWriter` behavior and Javadoc
---
.../org/apache/commons/io/output/StringBuilderWriter.java | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java b/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java
index 836e8bc3753..bdb757cf684 100644
--- a/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java
+++ b/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java
@@ -148,11 +148,14 @@ public String toString() {
* @param value The value to write.
* @param offset The index of the first character.
* @param length The number of characters to write.
+ * @throws IndexOutOfBoundsException if {@code offset} or {@code length} are negative, or if {@code offset + length} is greater than {@code value.length}.
*/
@Override
public void write(final char[] value, final int offset, final int length) {
- IOUtils.checkFromIndexSize(value, offset, length);
- builder.append(value, offset, length);
+ if (value != null) {
+ IOUtils.checkFromIndexSize(value, offset, length);
+ builder.append(value, offset, length);
+ }
}
/**
From b80971821c227b09de4e2aec5a2adcee7b4b4a3b Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 22:27:41 +0200
Subject: [PATCH 22/25] fix: restore `NullWriter` comment
---
src/main/java/org/apache/commons/io/output/NullWriter.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/main/java/org/apache/commons/io/output/NullWriter.java b/src/main/java/org/apache/commons/io/output/NullWriter.java
index 0ef9a09cdbf..635ffb044a6 100644
--- a/src/main/java/org/apache/commons/io/output/NullWriter.java
+++ b/src/main/java/org/apache/commons/io/output/NullWriter.java
@@ -97,6 +97,7 @@ public Writer append(final CharSequence csq) {
@Override
public Writer append(final CharSequence csq, final int start, final int end) {
IOUtils.checkFromToIndex(csq, start, end);
+ //to /dev/null
return this;
}
From a70be15bbb8c493a95144542f5a8fd8788e5b8cd Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 22:32:30 +0200
Subject: [PATCH 23/25] fix: `checkFromToIndex` logical or
---
src/main/java/org/apache/commons/io/IOUtils.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/apache/commons/io/IOUtils.java b/src/main/java/org/apache/commons/io/IOUtils.java
index 3f257692606..a882d4d87cb 100644
--- a/src/main/java/org/apache/commons/io/IOUtils.java
+++ b/src/main/java/org/apache/commons/io/IOUtils.java
@@ -578,7 +578,7 @@ public static void checkFromToIndex(final CharSequence seq, final int fromIndex,
}
static void checkFromToIndex(final int fromIndex, final int toIndex, final int length) {
- if (fromIndex < 0 | toIndex < fromIndex | length < toIndex) {
+ if (fromIndex < 0 || toIndex < fromIndex || length < toIndex) {
throw new IndexOutOfBoundsException(String.format("Range [%s, %s) out of bounds for length %s", fromIndex, toIndex, length));
}
}
From 8d520f72c3698440e2275b860a991a41757ad2fb Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 22:50:40 +0200
Subject: [PATCH 24/25] fix: document `IndexOutOfBoundsException`
---
.../java/org/apache/commons/io/input/BOMInputStream.java | 8 +++++++-
.../java/org/apache/commons/io/input/BoundedReader.java | 2 ++
.../org/apache/commons/io/input/CharSequenceReader.java | 5 +++--
src/main/java/org/apache/commons/io/input/NullReader.java | 2 ++
.../org/apache/commons/io/input/ReaderInputStream.java | 5 ++++-
.../java/org/apache/commons/io/input/SequenceReader.java | 5 -----
.../org/apache/commons/io/output/AppendableWriter.java | 6 ++++++
.../org/apache/commons/io/output/ChunkedOutputStream.java | 3 +++
.../java/org/apache/commons/io/output/ChunkedWriter.java | 3 +++
.../org/apache/commons/io/output/WriterOutputStream.java | 3 +++
.../org/apache/commons/io/output/XmlStreamWriter.java | 3 +++
11 files changed, 36 insertions(+), 9 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/input/BOMInputStream.java b/src/main/java/org/apache/commons/io/input/BOMInputStream.java
index 4cb347f1dc5..acfe8d9ddbb 100644
--- a/src/main/java/org/apache/commons/io/input/BOMInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/BOMInputStream.java
@@ -419,8 +419,10 @@ public int read() throws IOException {
* Invokes the delegate's {@code read(byte[])} method, detecting and optionally skipping BOM.
*
* @param buf
- * the buffer to read the bytes into
+ * the buffer to read the bytes into, never {@code null}
* @return the number of bytes read (excluding BOM) or -1 if the end of stream
+ * @throws NullPointerException
+ * if the buffer is {@code null}
* @throws IOException
* if an I/O error occurs
*/
@@ -439,6 +441,10 @@ public int read(final byte[] buf) throws IOException {
* @param len
* The number of bytes to read (excluding BOM)
* @return the number of bytes read or -1 if the end of stream
+ * @throws NullPointerException
+ * if the buffer is {@code null}
+ * @throws IndexOutOfBoundsException
+ * if {@code off} or {@code len} are negative, or if {@code off + len} is greater than {@code buf.length}
* @throws IOException
* if an I/O error occurs
*/
diff --git a/src/main/java/org/apache/commons/io/input/BoundedReader.java b/src/main/java/org/apache/commons/io/input/BoundedReader.java
index c64759045f2..5c1c255c0fe 100644
--- a/src/main/java/org/apache/commons/io/input/BoundedReader.java
+++ b/src/main/java/org/apache/commons/io/input/BoundedReader.java
@@ -118,6 +118,8 @@ public int read() throws IOException {
* @param off The offset
* @param len The number of chars to read
* @return the number of chars read
+ * @throws NullPointerException if the buffer is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code off} or {@code len} are negative, or if {@code off + len} is greater than {@code cbuf.length}.
* @throws IOException If an I/O error occurs while calling the underlying reader's read method
* @see Reader#read(char[], int, int)
*/
diff --git a/src/main/java/org/apache/commons/io/input/CharSequenceReader.java b/src/main/java/org/apache/commons/io/input/CharSequenceReader.java
index bc3defe7ddc..bb2be3692c9 100644
--- a/src/main/java/org/apache/commons/io/input/CharSequenceReader.java
+++ b/src/main/java/org/apache/commons/io/input/CharSequenceReader.java
@@ -205,8 +205,9 @@ public int read() {
* @param array The array to store the characters in
* @param offset The starting position in the array to store
* @param length The maximum number of characters to read
- * @return The number of characters read or -1 if there are
- * no more
+ * @return The number of characters read or -1 if there are no more
+ * @throws NullPointerException if the array is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code offset} or {@code length} are negative, or if {@code offset + length} is greater than {@code array.length}.
*/
@Override
public int read(final char[] array, final int offset, final int length) {
diff --git a/src/main/java/org/apache/commons/io/input/NullReader.java b/src/main/java/org/apache/commons/io/input/NullReader.java
index 2b7fdb47175..28f3d37eb21 100644
--- a/src/main/java/org/apache/commons/io/input/NullReader.java
+++ b/src/main/java/org/apache/commons/io/input/NullReader.java
@@ -272,6 +272,8 @@ public int read(final char[] chars) throws IOException {
* @return The number of characters read or {@code -1}
* if the end of file has been reached and
* {@code throwEofException} is set to {@code false}.
+ * @throws NullPointerException if the array is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code offset} or {@code length} are negative, or if {@code offset + length} is greater than {@code chars.length}.
* @throws EOFException if the end of file is reached and
* {@code throwEofException} is set to {@code true}.
* @throws IOException if trying to read past the end of file.
diff --git a/src/main/java/org/apache/commons/io/input/ReaderInputStream.java b/src/main/java/org/apache/commons/io/input/ReaderInputStream.java
index b2b6a82befb..4d22d28f891 100644
--- a/src/main/java/org/apache/commons/io/input/ReaderInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/ReaderInputStream.java
@@ -435,8 +435,9 @@ public int read() throws IOException {
/**
* Reads the specified number of bytes into an array.
*
- * @param b the byte array to read into
+ * @param b the byte array to read into, must not be {@code null}
* @return the number of bytes read or {@code -1} if the end of the stream has been reached
+ * @throws NullPointerException if the byte array is {@code null}.
* @throws IOException if an I/O error occurs.
*/
@Override
@@ -451,6 +452,8 @@ public int read(final byte[] b) throws IOException {
* @param off the offset to start reading bytes into
* @param len the number of bytes to read
* @return the number of bytes read or {@code -1} if the end of the stream has been reached
+ * @throws NullPointerException if the byte array is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code off} or {@code len} are negative, or if {@code off + len} is greater than {@code array.length}.
* @throws IOException if an I/O error occurs.
*/
@Override
diff --git a/src/main/java/org/apache/commons/io/input/SequenceReader.java b/src/main/java/org/apache/commons/io/input/SequenceReader.java
index b10bfcefcc9..daeb71faa92 100644
--- a/src/main/java/org/apache/commons/io/input/SequenceReader.java
+++ b/src/main/java/org/apache/commons/io/input/SequenceReader.java
@@ -108,11 +108,6 @@ public int read() throws IOException {
return c;
}
- /*
- * (non-Javadoc)
- *
- * @see Reader#read()
- */
@Override
public int read(final char[] cbuf, int off, int len) throws IOException {
IOUtils.checkFromIndexSize(cbuf, off, len);
diff --git a/src/main/java/org/apache/commons/io/output/AppendableWriter.java b/src/main/java/org/apache/commons/io/output/AppendableWriter.java
index a6f4ff53fa9..b1ace0b908c 100644
--- a/src/main/java/org/apache/commons/io/output/AppendableWriter.java
+++ b/src/main/java/org/apache/commons/io/output/AppendableWriter.java
@@ -79,6 +79,8 @@ public Writer append(final CharSequence csq) throws IOException {
* @param start the index of the first character in the subsequence
* @param end the index of the character following the last character in the subsequence
* @return this writer
+ * @throws IndexOutOfBoundsException If {@code start} or {@code end} are negative, {@code start} is greater than
+ * {@code end}, or {@code end} is greater than {@code csq.length()}.
* @throws IOException If an I/O error occurs.
*/
@Override
@@ -122,6 +124,8 @@ public T getAppendable() {
* @param cbuf an array with the characters to write.
* @param off offset from which to start writing characters.
* @param len number of characters to write.
+ * @throws NullPointerException if the array is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code off} or {@code len} are negative, or if {@code off + len} is greater than {@code cbuf.length}.
* @throws IOException If an I/O error occurs.
*/
@Override
@@ -149,6 +153,8 @@ public void write(final int c) throws IOException {
* @param str a string.
* @param off offset from which to start writing characters.
* @param len number of characters to write.
+ * @throws NullPointerException if the string is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code off} or {@code len} are negative, or if {@code off + len} is greater than {@code str.length()}.
* @throws IOException If an I/O error occurs.
*/
@Override
diff --git a/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java b/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java
index adecd946a3e..2588264e172 100644
--- a/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java
@@ -168,6 +168,9 @@ int getChunkSize() {
* @param data the data to write.
* @param srcOffset the offset.
* @param length the length of data to write.
+ * @throws NullPointerException if the data is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code srcOffset} or {@code length} are negative,
+ * or if {@code srcOffset + length} is greater than {@code data.length}.
* @throws IOException if an I/O error occurs.
*/
@Override
diff --git a/src/main/java/org/apache/commons/io/output/ChunkedWriter.java b/src/main/java/org/apache/commons/io/output/ChunkedWriter.java
index f18553fb776..ed22625b667 100644
--- a/src/main/java/org/apache/commons/io/output/ChunkedWriter.java
+++ b/src/main/java/org/apache/commons/io/output/ChunkedWriter.java
@@ -70,6 +70,9 @@ public ChunkedWriter(final Writer writer, final int chunkSize) {
* @param data The data.
* @param srcOffset the offset.
* @param length the number of bytes to write.
+ * @throws NullPointerException if the data is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code srcOffset} or {@code length} are negative,
+ * or if {@code srcOffset + length} is greater than {@code data.length}.
* @throws IOException If an I/O error occurs.
*/
@Override
diff --git a/src/main/java/org/apache/commons/io/output/WriterOutputStream.java b/src/main/java/org/apache/commons/io/output/WriterOutputStream.java
index c753ccdce87..d17318f79b6 100644
--- a/src/main/java/org/apache/commons/io/output/WriterOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/WriterOutputStream.java
@@ -431,6 +431,7 @@ private void processInput(final boolean endOfInput) throws IOException {
* Writes bytes from the specified byte array to the stream.
*
* @param b the byte array containing the bytes to write.
+ * @throws NullPointerException if the byte array is {@code null}.
* @throws IOException if an I/O error occurs.
*/
@Override
@@ -444,6 +445,8 @@ public void write(final byte[] b) throws IOException {
* @param b the byte array containing the bytes to write.
* @param off the start offset in the byte array.
* @param len the number of bytes to write.
+ * @throws NullPointerException if the byte array is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code off} or {@code len} are negative, or if {@code off + len} is greater than {@code b.length}.
* @throws IOException if an I/O error occurs.
*/
@Override
diff --git a/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java b/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java
index 73630a4bca2..873df0238d8 100644
--- a/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java
+++ b/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java
@@ -302,6 +302,9 @@ public String getEncoding() {
* @param cbuf the buffer to write the characters from.
* @param off The start offset.
* @param len The number of characters to write.
+ * @throws NullPointerException if the buffer is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code off} or {@code len} are negative,
+ * or if {@code off + len} is greater than {@code cbuf.length}.
* @throws IOException if an error occurs detecting the encoding.
*/
@Override
From c4e4f3b5b09d59d0078a7d8b3f3772dea728e06f Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 1 Oct 2025 22:59:46 +0200
Subject: [PATCH 25/25] fix: `ClosedInputStream` and `ThresholdingOutputStream`
javadoc
---
.../org/apache/commons/io/input/ClosedInputStream.java | 10 ++++++----
.../commons/io/output/ThresholdingOutputStream.java | 3 +++
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/input/ClosedInputStream.java b/src/main/java/org/apache/commons/io/input/ClosedInputStream.java
index 5ddb9ea6c9d..11bc7be6e19 100644
--- a/src/main/java/org/apache/commons/io/input/ClosedInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/ClosedInputStream.java
@@ -79,10 +79,12 @@ public int read() {
/**
* Returns {@code -1} to indicate that the stream is closed.
*
- * @param b ignored.
- * @param off ignored.
- * @param len ignored.
- * @return always -1.
+ * @param b The buffer to read bytes into.
+ * @param off The start offset.
+ * @param len The number of bytes to read.
+ * @return If len is zero, then {@code 0}; otherwise {@code -1}.
+ * @throws NullPointerException if the byte array is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code off} or {@code len} are negative, or if {@code off + len} is greater than {@code b.length}.
*/
@Override
public int read(final byte[] b, final int off, final int len) throws IOException {
diff --git a/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java b/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java
index 2efe401910a..742572b2ebc 100644
--- a/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java
@@ -228,6 +228,7 @@ protected void thresholdReached() throws IOException {
* Writes {@code b.length} bytes from the specified byte array to this output stream.
*
* @param b The array of bytes to be written.
+ * @throws NullPointerException if the byte array is {@code null}.
* @throws IOException if an error occurs.
*/
@SuppressWarnings("resource") // the underlying stream is managed by a subclass.
@@ -245,6 +246,8 @@ public void write(final byte[] b) throws IOException {
* @param b The byte array from which the data will be written.
* @param off The start offset in the byte array.
* @param len The number of bytes to write.
+ * @throws NullPointerException if the byte array is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code off} or {@code len} are negative, or if {@code off + len} is greater than {@code b.length}.
* @throws IOException if an error occurs.
*/
@SuppressWarnings("resource") // the underlying stream is managed by a subclass.