diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 791a08b05d7..b74dca11eef 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -54,6 +54,8 @@ The type attribute can be add,update,fix,remove.
Add ThrottledInputStream.Builder.setMaxBytes(long, ChronoUnit).
Add IOIterable.
ReversedLinesFileReader implements IOIterable<String>.
+ Add AbstractByteArrayOutputStream.write(CharSequence, Charset).
+ Add AbstractByteArrayOutputStream.write(byte[]).
diff --git a/src/main/java/org/apache/commons/io/output/AbstractByteArrayOutputStream.java b/src/main/java/org/apache/commons/io/output/AbstractByteArrayOutputStream.java
index f3d08d0e706..30e259c28eb 100644
--- a/src/main/java/org/apache/commons/io/output/AbstractByteArrayOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/AbstractByteArrayOutputStream.java
@@ -28,6 +28,7 @@
import java.util.Collections;
import java.util.List;
+import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ClosedInputStream;
@@ -55,9 +56,10 @@
* ignored.
*
*
+ * @param The AbstractByteArrayOutputStream subclass
* @since 2.7
*/
-public abstract class AbstractByteArrayOutputStream extends OutputStream {
+public abstract class AbstractByteArrayOutputStream> extends OutputStream {
/**
* Constructor for an InputStream subclass.
@@ -83,18 +85,18 @@ protected interface InputStreamConstructor {
/** The list of buffers, which grows and never reduces. */
private final List buffers = new ArrayList<>();
+ /** The total count of bytes written. */
+ protected int count;
+
+ /** The current buffer. */
+ private byte[] currentBuffer;
+
/** The index of the current buffer. */
private int currentBufferIndex;
/** The total count of bytes in all the filled buffers. */
private int filledBufferSum;
- /** The current buffer. */
- private byte[] currentBuffer;
-
- /** The total count of bytes written. */
- protected int count;
-
/** Flag to indicate if the buffers can be reused after reset */
private boolean reuseBuffers = true;
@@ -105,6 +107,16 @@ public AbstractByteArrayOutputStream() {
// empty
}
+ /*
+ * Returns this instance typed to {@code T}.
+ *
+ * @return this instance
+ */
+ @SuppressWarnings("unchecked")
+ protected T asThis() {
+ return (T) this;
+ }
+
/**
* Does nothing.
*
@@ -304,9 +316,34 @@ public String toString(final String enc) throws UnsupportedEncodingException {
return new String(toByteArray(), enc);
}
+ /**
+ * Writes {@code b.length} bytes from the given byte array to this output stream. This has same effect as {@code write(b, 0, b.length)}.
+ *
+ * @param b the data.
+ * @see #write(byte[], int, int)
+ * @since 2.19.0
+ */
+ @Override
+ public void write(final byte b[]) {
+ write(b, 0, b.length);
+ }
+
@Override
public abstract void write(final byte[] b, final int off, final int len);
+ /**
+ * Writes the bytes for given CharSequence encoded using a Charset.
+ *
+ * @param data The String to convert to bytes. not null.
+ * @param charset The {@link Charset} o encode the {@code String}, null means the default encoding.
+ * @return this instance.
+ * @since 2.19.0
+ */
+ public T write(final CharSequence data, final Charset charset) {
+ write(data.toString().getBytes(Charsets.toCharset(charset)));
+ return asThis();
+ }
+
/**
* Writes the entire contents of the specified input stream to this
* byte stream. Bytes from the input stream are read directly into the
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 b2b310ab79c..285c3f45d84 100644
--- a/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java
@@ -25,7 +25,7 @@
* Implements a ThreadSafe version of {@link AbstractByteArrayOutputStream} using instance synchronization.
*/
//@ThreadSafe
-public class ByteArrayOutputStream extends AbstractByteArrayOutputStream {
+public class ByteArrayOutputStream extends AbstractByteArrayOutputStream {
/**
* Fetches entire contents of an {@link InputStream} and represent
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 429d8162cf0..cc0b455c039 100644
--- a/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
@@ -36,7 +36,7 @@
* @since 2.7
*/
//@NotThreadSafe
-public final class UnsynchronizedByteArrayOutputStream extends AbstractByteArrayOutputStream {
+public final class UnsynchronizedByteArrayOutputStream extends AbstractByteArrayOutputStream {
// @formatter:off
/**
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 9e9e4cfb2e5..f700892ce50 100644
--- a/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTest.java
@@ -27,6 +27,8 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.stream.Stream;
import org.apache.commons.io.IOUtils;
@@ -41,7 +43,8 @@
*/
public class ByteArrayOutputStreamTest {
- private interface BAOSFactory {
+ private interface BAOSFactory> {
+
T newInstance();
T newInstance(final int size);
@@ -71,18 +74,18 @@ public UnsynchronizedByteArrayOutputStream newInstance(final int size) {
}
}
- private static final byte[] DATA;
+ private static final byte[] ASCII_DATA;
static {
- DATA = new byte[64];
- for (byte i = 0; i < 64; i++) {
- DATA[i] = i;
+ ASCII_DATA = new byte[64];
+ for (byte i = 0; i < ASCII_DATA.length; i++) {
+ ASCII_DATA[i] = (byte) (char) ('0' + i);
}
}
private static Stream baosFactories() {
return Stream.of(Arguments.of(ByteArrayOutputStream.class.getSimpleName(), new ByteArrayOutputStreamFactory()),
- Arguments.of(UnsynchronizedByteArrayOutputStream.class.getSimpleName(), new UnsynchronizedByteArrayOutputStreamFactory()));
+ Arguments.of(UnsynchronizedByteArrayOutputStream.class.getSimpleName(), new UnsynchronizedByteArrayOutputStreamFactory()));
}
private static boolean byteCmp(final byte[] src, final byte[] cmp) {
@@ -99,12 +102,12 @@ private static Stream toBufferedInputStreamFunctionFactories() {
final IOFunction syncBaosToBufferedInputStreamWithSize = is -> ByteArrayOutputStream.toBufferedInputStream(is, 1024);
final IOFunction unSyncBaosToBufferedInputStream = UnsynchronizedByteArrayOutputStream::toBufferedInputStream;
final IOFunction unSyncBaosToBufferedInputStreamWithSize = is -> UnsynchronizedByteArrayOutputStream.toBufferedInputStream(is,
- 1024);
+ 1024);
return Stream.of(Arguments.of("ByteArrayOutputStream.toBufferedInputStream(InputStream)", syncBaosToBufferedInputStream),
- Arguments.of("ByteArrayOutputStream.toBufferedInputStream(InputStream, int)", syncBaosToBufferedInputStreamWithSize),
- Arguments.of("UnsynchronizedByteArrayOutputStream.toBufferedInputStream(InputStream)", unSyncBaosToBufferedInputStream),
- Arguments.of("UnsynchronizedByteArrayOutputStream.toBufferedInputStream(InputStream, int)", unSyncBaosToBufferedInputStreamWithSize));
+ Arguments.of("ByteArrayOutputStream.toBufferedInputStream(InputStream, int)", syncBaosToBufferedInputStreamWithSize),
+ Arguments.of("UnsynchronizedByteArrayOutputStream.toBufferedInputStream(InputStream)", unSyncBaosToBufferedInputStream),
+ Arguments.of("UnsynchronizedByteArrayOutputStream.toBufferedInputStream(InputStream, int)", unSyncBaosToBufferedInputStreamWithSize));
}
private void checkByteArrays(final byte[] expected, final byte[] actual) {
@@ -116,7 +119,7 @@ private void checkByteArrays(final byte[] expected, final byte[] actual) {
}
}
- private void checkStreams(final AbstractByteArrayOutputStream actual, final java.io.ByteArrayOutputStream expected) {
+ private void checkStreams(final AbstractByteArrayOutputStream> actual, final java.io.ByteArrayOutputStream expected) {
assertEquals(expected.size(), actual.size(), "Sizes are not equal");
final byte[] buf = actual.toByteArray();
final byte[] refbuf = expected.toByteArray();
@@ -132,7 +135,7 @@ public void testInvalidParameterizedConstruction(final String baosName, final BA
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("baosFactories")
public void testInvalidWriteLenUnder(final String baosName, final BAOSFactory> baosFactory) throws IOException {
- try (AbstractByteArrayOutputStream baout = baosFactory.newInstance()) {
+ try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance()) {
assertThrows(IndexOutOfBoundsException.class, () -> baout.write(new byte[1], 0, -1));
}
}
@@ -140,7 +143,7 @@ public void testInvalidWriteLenUnder(final String baosName, final BAOSFactory>
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("baosFactories")
public void testInvalidWriteOffsetAndLenOver(final String baosName, final BAOSFactory> baosFactory) throws IOException {
- try (AbstractByteArrayOutputStream baout = baosFactory.newInstance()) {
+ try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance()) {
assertThrows(IndexOutOfBoundsException.class, () -> baout.write(new byte[1], 0, 2));
}
}
@@ -148,7 +151,7 @@ public void testInvalidWriteOffsetAndLenOver(final String baosName, final BAOSFa
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("baosFactories")
public void testInvalidWriteOffsetAndLenUnder(final String baosName, final BAOSFactory> baosFactory) throws IOException {
- try (AbstractByteArrayOutputStream baout = baosFactory.newInstance()) {
+ try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance()) {
assertThrows(IndexOutOfBoundsException.class, () -> baout.write(new byte[1], 1, -2));
}
}
@@ -156,7 +159,7 @@ public void testInvalidWriteOffsetAndLenUnder(final String baosName, final BAOSF
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("baosFactories")
public void testInvalidWriteOffsetOver(final String baosName, final BAOSFactory> baosFactory) throws IOException {
- try (AbstractByteArrayOutputStream baout = baosFactory.newInstance()) {
+ try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance()) {
assertThrows(IndexOutOfBoundsException.class, () -> baout.write(IOUtils.EMPTY_BYTE_ARRAY, 1, 0));
}
}
@@ -164,73 +167,15 @@ public void testInvalidWriteOffsetOver(final String baosName, final BAOSFactory<
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("baosFactories")
public void testInvalidWriteOffsetUnder(final String baosName, final BAOSFactory> baosFactory) throws IOException {
- try (AbstractByteArrayOutputStream baout = baosFactory.newInstance()) {
+ try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance()) {
assertThrows(IndexOutOfBoundsException.class, () -> baout.write(null, -1, 0));
}
}
- @ParameterizedTest(name = "[{index}] {0}")
- @MethodSource("baosFactories")
- public void testStream(final String baosName, final BAOSFactory> baosFactory) throws Exception {
- int written;
-
- // The ByteArrayOutputStream is initialized with 32 bytes to match
- // the original more closely for this test.
- try (AbstractByteArrayOutputStream baout = baosFactory.newInstance(32);
- final java.io.ByteArrayOutputStream ref = new java.io.ByteArrayOutputStream()) {
-
- // First three writes
- written = writeData(baout, ref, new int[] {4, 10, 22});
- assertEquals(36, written);
- checkStreams(baout, ref);
-
- // Another two writes to see if there are any bad effects after toByteArray()
- written = writeData(baout, ref, new int[] {20, 12});
- assertEquals(32, written);
- checkStreams(baout, ref);
-
- // Now reset the streams
- baout.reset();
- ref.reset();
-
- // Test again to see if reset() had any bad effects
- written = writeData(baout, ref, new int[] {5, 47, 33, 60, 1, 0, 8});
- assertEquals(155, written);
- checkStreams(baout, ref);
-
- // Test the readFrom(InputStream) method
- baout.reset();
- written = baout.write(new ByteArrayInputStream(ref.toByteArray()));
- assertEquals(155, written);
- checkStreams(baout, ref);
-
- // Write the commons Byte[]OutputStream to a java.io.Byte[]OutputStream
- // and vice-versa to test the writeTo() method.
- try (AbstractByteArrayOutputStream baout1 = baosFactory.newInstance(32)) {
- ref.writeTo(baout1);
- final java.io.ByteArrayOutputStream ref1 = new java.io.ByteArrayOutputStream();
- baout.writeTo(ref1);
- checkStreams(baout1, ref1);
-
- // Testing toString(String)
- final String baoutString = baout.toString("ASCII");
- final String refString = ref.toString("ASCII");
- assertEquals(refString, baoutString, "ASCII decoded String must be equal");
-
- // Make sure that empty ByteArrayOutputStreams really don't create garbage
- // on toByteArray()
- try (AbstractByteArrayOutputStream baos1 = baosFactory.newInstance();
- final AbstractByteArrayOutputStream baos2 = baosFactory.newInstance()) {
- assertSame(baos1.toByteArray(), baos2.toByteArray());
- }
- }
- }
- }
-
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("toBufferedInputStreamFunctionFactories")
public void testToBufferedInputStream(final String baosName, final IOFunction toBufferedInputStreamFunction) throws IOException {
- final byte[] data = {(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE};
+ final byte[] data = { (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE };
try (ByteArrayInputStream bain = new ByteArrayInputStream(data)) {
assertEquals(data.length, bain.available());
@@ -247,7 +192,7 @@ public void testToBufferedInputStream(final String baosName, final IOFunction toBufferedInputStreamFunction)
- throws IOException {
+ throws IOException {
try (ByteArrayInputStream bain = new ByteArrayInputStream(IOUtils.EMPTY_BYTE_ARRAY)) {
assertEquals(0, bain.available());
@@ -261,13 +206,13 @@ public void testToBufferedInputStreamEmpty(final String baosName, final IOFuncti
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("baosFactories")
public void testToInputStream(final String baosName, final BAOSFactory> baosFactory) throws IOException {
- try (AbstractByteArrayOutputStream baout = baosFactory.newInstance();
- final java.io.ByteArrayOutputStream ref = new java.io.ByteArrayOutputStream()) {
+ try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance();
+ final java.io.ByteArrayOutputStream ref = new java.io.ByteArrayOutputStream()) {
// Write 8224 bytes
- writeData(baout, ref, 32);
+ writeByteArrayIndex(baout, ref, 32);
for (int i = 0; i < 128; i++) {
- writeData(baout, ref, 64);
+ writeByteArrayIndex(baout, ref, 64);
}
// Get data before more writes
@@ -275,7 +220,7 @@ public void testToInputStream(final String baosName, final BAOSFactory> baosFa
byte[] refData = ref.toByteArray();
// Write some more data
- writeData(baout, ref, new int[] {2, 4, 8, 16});
+ writeByteArrayIndex(baout, ref, new int[] { 2, 4, 8, 16 });
// Check original data
byte[] baoutData = IOUtils.toByteArray(in);
@@ -296,9 +241,9 @@ public void testToInputStream(final String baosName, final BAOSFactory> baosFa
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("baosFactories")
public void testToInputStreamEmpty(final String baosName, final BAOSFactory> baosFactory) throws IOException {
- try (AbstractByteArrayOutputStream baout = baosFactory.newInstance();
- // Get data before more writes
- final InputStream in = baout.toInputStream()) {
+ try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance();
+ // Get data before more writes
+ final InputStream in = baout.toInputStream()) {
assertEquals(0, in.available());
assertInstanceOf(ClosedInputStream.class, in);
}
@@ -308,13 +253,13 @@ public void testToInputStreamEmpty(final String baosName, final BAOSFactory> b
@MethodSource("baosFactories")
public void testToInputStreamWithReset(final String baosName, final BAOSFactory> baosFactory) throws IOException {
// Make sure reset() do not destroy InputStream returned from toInputStream()
- try (AbstractByteArrayOutputStream baout = baosFactory.newInstance();
- final java.io.ByteArrayOutputStream ref = new java.io.ByteArrayOutputStream()) {
+ try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance();
+ final java.io.ByteArrayOutputStream ref = new java.io.ByteArrayOutputStream()) {
// Write 8224 bytes
- writeData(baout, ref, 32);
+ writeByteArrayIndex(baout, ref, 32);
for (int i = 0; i < 128; i++) {
- writeData(baout, ref, 64);
+ writeByteArrayIndex(baout, ref, 64);
}
// Get data before reset
@@ -324,7 +269,7 @@ public void testToInputStreamWithReset(final String baosName, final BAOSFactory<
// Reset and write some new data
baout.reset();
ref.reset();
- writeData(baout, ref, new int[] {2, 4, 8, 16});
+ writeByteArrayIndex(baout, ref, new int[] { 2, 4, 8, 16 });
// Check original data
byte[] baoutData = IOUtils.toByteArray(in);
@@ -342,17 +287,251 @@ public void testToInputStreamWithReset(final String baosName, final BAOSFactory<
}
}
+ @ParameterizedTest(name = "[{index}] {0}")
+ @MethodSource("baosFactories")
+ public void testWriteByte(final String baosName, final BAOSFactory> baosFactory) throws Exception {
+ int written;
+
+ // The ByteArrayOutputStream is initialized with 32 bytes to match
+ // the original more closely for this test.
+ try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance(32);
+ final java.io.ByteArrayOutputStream ref = new java.io.ByteArrayOutputStream()) {
+
+ // First three writes
+ written = writeByte(baout, ref, new int[] { 4, 10, 22 });
+ assertEquals(36, written);
+ checkStreams(baout, ref);
+
+ // Another two writes to see if there are any bad effects after toByteArray()
+ written = writeByte(baout, ref, new int[] { 20, 12 });
+ assertEquals(32, written);
+ checkStreams(baout, ref);
+
+ // Now reset the streams
+ baout.reset();
+ ref.reset();
+
+ // Test again to see if reset() had any bad effects
+ written = writeByte(baout, ref, new int[] { 5, 47, 33, 60, 1, 0, 8 });
+ assertEquals(155, written);
+ checkStreams(baout, ref);
+
+ // Test the readFrom(InputStream) method
+ baout.reset();
+ written = baout.write(new ByteArrayInputStream(ref.toByteArray()));
+ assertEquals(155, written);
+ checkStreams(baout, ref);
+
+ // Write the commons Byte[]OutputStream to a java.io.Byte[]OutputStream
+ // and vice-versa to test the writeTo() method.
+ try (AbstractByteArrayOutputStream> baout1 = baosFactory.newInstance(32)) {
+ ref.writeTo(baout1);
+ final java.io.ByteArrayOutputStream ref1 = new java.io.ByteArrayOutputStream();
+ baout.writeTo(ref1);
+ checkStreams(baout1, ref1);
+
+ // Testing toString(String)
+ final String baoutString = baout.toString("ASCII");
+ final String refString = ref.toString("ASCII");
+ assertEquals(refString, baoutString, "ASCII decoded String must be equal");
+
+ // Make sure that empty ByteArrayOutputStreams really don't create garbage
+ // on toByteArray()
+ try (AbstractByteArrayOutputStream> baos1 = baosFactory.newInstance();
+ final AbstractByteArrayOutputStream> baos2 = baosFactory.newInstance()) {
+ assertSame(baos1.toByteArray(), baos2.toByteArray());
+ }
+ }
+ }
+ }
+
+ // writeStringCharset
+
+ @ParameterizedTest(name = "[{index}] {0}")
+ @MethodSource("baosFactories")
+ public void testWriteByteArray(final String baosName, final BAOSFactory> baosFactory) throws Exception {
+ int written;
+
+ // The ByteArrayOutputStream is initialized with 32 bytes to match
+ // the original more closely for this test.
+ try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance(32);
+ final java.io.ByteArrayOutputStream ref = new java.io.ByteArrayOutputStream()) {
+
+ // First three writes
+ written = writeByteArray(baout, ref, new int[] { 4, 10, 22 });
+ assertEquals(36, written);
+ checkStreams(baout, ref);
+
+ // Another two writes to see if there are any bad effects after toByteArray()
+ written = writeByteArray(baout, ref, new int[] { 20, 12 });
+ assertEquals(32, written);
+ checkStreams(baout, ref);
+
+ // Now reset the streams
+ baout.reset();
+ ref.reset();
+
+ // Test again to see if reset() had any bad effects
+ written = writeByteArray(baout, ref, new int[] { 5, 47, 33, 60, 1, 0, 8 });
+ assertEquals(155, written);
+ checkStreams(baout, ref);
+
+ // Test the readFrom(InputStream) method
+ baout.reset();
+ written = baout.write(new ByteArrayInputStream(ref.toByteArray()));
+ assertEquals(155, written);
+ checkStreams(baout, ref);
+
+ // Write the commons Byte[]OutputStream to a java.io.Byte[]OutputStream
+ // and vice-versa to test the writeTo() method.
+ try (AbstractByteArrayOutputStream> baout1 = baosFactory.newInstance(32)) {
+ ref.writeTo(baout1);
+ final java.io.ByteArrayOutputStream ref1 = new java.io.ByteArrayOutputStream();
+ baout.writeTo(ref1);
+ checkStreams(baout1, ref1);
+
+ // Testing toString(String)
+ final String baoutString = baout.toString("ASCII");
+ final String refString = ref.toString("ASCII");
+ assertEquals(refString, baoutString, "ASCII decoded String must be equal");
+
+ // Make sure that empty ByteArrayOutputStreams really don't create garbage
+ // on toByteArray()
+ try (AbstractByteArrayOutputStream> baos1 = baosFactory.newInstance();
+ final AbstractByteArrayOutputStream> baos2 = baosFactory.newInstance()) {
+ assertSame(baos1.toByteArray(), baos2.toByteArray());
+ }
+ }
+ }
+ }
+
+ @ParameterizedTest(name = "[{index}] {0}")
+ @MethodSource("baosFactories")
+ public void testWriteByteArrayIndex(final String baosName, final BAOSFactory> baosFactory) throws Exception {
+ int written;
+
+ // The ByteArrayOutputStream is initialized with 32 bytes to match
+ // the original more closely for this test.
+ try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance(32);
+ final java.io.ByteArrayOutputStream ref = new java.io.ByteArrayOutputStream()) {
+
+ // First three writes
+ written = writeByteArrayIndex(baout, ref, new int[] { 4, 10, 22 });
+ assertEquals(36, written);
+ checkStreams(baout, ref);
+
+ // Another two writes to see if there are any bad effects after toByteArray()
+ written = writeByteArrayIndex(baout, ref, new int[] { 20, 12 });
+ assertEquals(32, written);
+ checkStreams(baout, ref);
+
+ // Now reset the streams
+ baout.reset();
+ ref.reset();
+
+ // Test again to see if reset() had any bad effects
+ written = writeByteArrayIndex(baout, ref, new int[] { 5, 47, 33, 60, 1, 0, 8 });
+ assertEquals(155, written);
+ checkStreams(baout, ref);
+
+ // Test the readFrom(InputStream) method
+ baout.reset();
+ written = baout.write(new ByteArrayInputStream(ref.toByteArray()));
+ assertEquals(155, written);
+ checkStreams(baout, ref);
+
+ // Write the commons Byte[]OutputStream to a java.io.Byte[]OutputStream
+ // and vice-versa to test the writeTo() method.
+ try (AbstractByteArrayOutputStream> baout1 = baosFactory.newInstance(32)) {
+ ref.writeTo(baout1);
+ final java.io.ByteArrayOutputStream ref1 = new java.io.ByteArrayOutputStream();
+ baout.writeTo(ref1);
+ checkStreams(baout1, ref1);
+
+ // Testing toString(String)
+ final String baoutString = baout.toString("ASCII");
+ final String refString = ref.toString("ASCII");
+ assertEquals(refString, baoutString, "ASCII decoded String must be equal");
+
+ // Make sure that empty ByteArrayOutputStreams really don't create garbage
+ // on toByteArray()
+ try (AbstractByteArrayOutputStream> baos1 = baosFactory.newInstance();
+ final AbstractByteArrayOutputStream> baos2 = baosFactory.newInstance()) {
+ assertSame(baos1.toByteArray(), baos2.toByteArray());
+ }
+ }
+ }
+ }
+
+ @ParameterizedTest(name = "[{index}] {0}")
+ @MethodSource("baosFactories")
+ public void testWriteStringCharset(final String baosName, final BAOSFactory> baosFactory) throws Exception {
+ int written;
+
+ // The ByteArrayOutputStream is initialized with 32 bytes to match
+ // the original more closely for this test.
+ try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance(32);
+ final java.io.ByteArrayOutputStream ref = new java.io.ByteArrayOutputStream()) {
+
+ // First three writes
+ written = writeStringCharset(baout, ref, new int[] { 4, 10, 22 });
+ assertEquals(36, written);
+ checkStreams(baout, ref);
+
+ // Another two writes to see if there are any bad effects after toByteArray()
+ written = writeStringCharset(baout, ref, new int[] { 20, 12 });
+ assertEquals(32, written);
+ checkStreams(baout, ref);
+
+ // Now reset the streams
+ baout.reset();
+ ref.reset();
+
+ // Test again to see if reset() had any bad effects
+ written = writeStringCharset(baout, ref, new int[] { 5, 47, 33, 60, 1, 0, 8 });
+ assertEquals(155, written);
+ checkStreams(baout, ref);
+
+ // Test the readFrom(InputStream) method
+ baout.reset();
+ written = baout.write(new ByteArrayInputStream(ref.toByteArray()));
+ assertEquals(155, written);
+ checkStreams(baout, ref);
+
+ // Write the commons Byte[]OutputStream to a java.io.Byte[]OutputStream
+ // and vice-versa to test the writeTo() method.
+ try (AbstractByteArrayOutputStream> baout1 = baosFactory.newInstance(32)) {
+ ref.writeTo(baout1);
+ final java.io.ByteArrayOutputStream ref1 = new java.io.ByteArrayOutputStream();
+ baout.writeTo(ref1);
+ checkStreams(baout1, ref1);
+
+ // Testing toString(String)
+ final String baoutString = baout.toString("ASCII");
+ final String refString = ref.toString("ASCII");
+ assertEquals(refString, baoutString, "ASCII decoded String must be equal");
+
+ // Make sure that empty ByteArrayOutputStreams really don't create garbage
+ // on toByteArray()
+ try (AbstractByteArrayOutputStream> baos1 = baosFactory.newInstance();
+ final AbstractByteArrayOutputStream> baos2 = baosFactory.newInstance()) {
+ assertSame(baos1.toByteArray(), baos2.toByteArray());
+ }
+ }
+ }
+ }
+
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("baosFactories")
public void testWriteZero(final String baosName, final BAOSFactory> baosFactory) throws IOException {
- try (AbstractByteArrayOutputStream baout = baosFactory.newInstance()) {
+ try (AbstractByteArrayOutputStream> baout = baosFactory.newInstance()) {
baout.write(IOUtils.EMPTY_BYTE_ARRAY, 0, 0);
assertTrue(true, "Dummy");
}
}
- private int writeData(final AbstractByteArrayOutputStream baout, final java.io.ByteArrayOutputStream ref, final int count) {
- if (count > DATA.length) {
+ private int writeByte(final AbstractByteArrayOutputStream> baout, final java.io.ByteArrayOutputStream ref, final int count) {
+ if (count > ASCII_DATA.length) {
throw new IllegalArgumentException("Requesting too many bytes");
}
if (count == 0) {
@@ -360,15 +539,94 @@ private int writeData(final AbstractByteArrayOutputStream baout, final java.io.B
ref.write(100);
return 1;
}
- baout.write(DATA, 0, count);
- ref.write(DATA, 0, count);
+ for (int i = 0; i < count; i++) {
+ baout.write(ASCII_DATA[i]);
+ ref.write(ASCII_DATA[i]);
+ }
+ return count;
+ }
+
+ private int writeByte(final AbstractByteArrayOutputStream> baout, final java.io.ByteArrayOutputStream ref, final int[] instructions) throws IOException {
+ int written = 0;
+ for (final int instruction : instructions) {
+ written += writeByte(baout, ref, instruction);
+ }
+ return written;
+ }
+
+ private int writeByteArray(final AbstractByteArrayOutputStream> baout, final java.io.ByteArrayOutputStream ref, final int count) throws IOException {
+ if (count > ASCII_DATA.length) {
+ throw new IllegalArgumentException("Requesting too many bytes");
+ }
+ if (count == 0) {
+ // length 1 data
+ baout.write(new byte[] { 100 });
+ ref.write(new byte[] { 100 });
+ return 1;
+ }
+ baout.write(Arrays.copyOf(ASCII_DATA, count));
+ ref.write(Arrays.copyOf(ASCII_DATA, count));
+ return count;
+ }
+
+ private int writeByteArray(final AbstractByteArrayOutputStream> baout, final java.io.ByteArrayOutputStream ref, final int[] instructions)
+ throws IOException {
+ int written = 0;
+ for (final int instruction : instructions) {
+ written += writeByteArray(baout, ref, instruction);
+ }
+ return written;
+ }
+
+ private int writeByteArrayIndex(final AbstractByteArrayOutputStream> baout, final java.io.ByteArrayOutputStream ref, final int count) {
+ if (count > ASCII_DATA.length) {
+ throw new IllegalArgumentException("Requesting too many bytes");
+ }
+ if (count == 0) {
+ // length 1 data
+ baout.write(100);
+ ref.write(100);
+ return 1;
+ }
+ baout.write(ASCII_DATA, 0, count);
+ ref.write(ASCII_DATA, 0, count);
+ return count;
+ }
+
+ private int writeByteArrayIndex(final AbstractByteArrayOutputStream> baout, final java.io.ByteArrayOutputStream ref, final int[] instructions) {
+ int written = 0;
+ for (final int instruction : instructions) {
+ written += writeByteArrayIndex(baout, ref, instruction);
+ }
+ return written;
+ }
+
+ private int writeStringCharset(final AbstractByteArrayOutputStream> baout, final java.io.ByteArrayOutputStream ref, final int count) throws IOException {
+ if (count > ASCII_DATA.length) {
+ throw new IllegalArgumentException("Requesting too many bytes");
+ }
+ if (count == 0) {
+ // length 1 data
+ final String data = "a";
+ baout.write(data, StandardCharsets.UTF_8);
+ ref.write(data.getBytes(StandardCharsets.UTF_8));
+ return 1;
+ }
+ final String data = new String(ASCII_DATA, StandardCharsets.UTF_8).substring(0, count);
+ assertEquals(count, data.length(), () -> String.format("[%,d]:'%s'", count, data));
+ baout.write(data, StandardCharsets.UTF_8);
+ final byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
+ // For now, test assumes 1-1 size mapping from chars to bytes.
+ assertEquals(count, bytes.length, () -> String.format("[%,d]:'%s'", count, data));
+ ref.write(bytes);
return count;
}
- private int writeData(final AbstractByteArrayOutputStream baout, final java.io.ByteArrayOutputStream ref, final int[] instructions) {
+ private int writeStringCharset(final AbstractByteArrayOutputStream> baout, final java.io.ByteArrayOutputStream ref, final int[] instructions)
+ throws IOException {
int written = 0;
for (final int instruction : instructions) {
- written += writeData(baout, ref, instruction);
+ written += writeStringCharset(baout, ref, instruction);
}
return written;
}
diff --git a/src/test/java/org/apache/commons/io/output/ProxyOutputStreamTest.java b/src/test/java/org/apache/commons/io/output/ProxyOutputStreamTest.java
index 8c3a2ec5a15..d5c86c67f6e 100644
--- a/src/test/java/org/apache/commons/io/output/ProxyOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/output/ProxyOutputStreamTest.java
@@ -17,9 +17,12 @@
package org.apache.commons.io.output;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.io.IOException;
import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -29,18 +32,26 @@
*/
public class ProxyOutputStreamTest {
+ private final AtomicBoolean hit = new AtomicBoolean();
+
private ByteArrayOutputStream original;
private OutputStream proxied;
@BeforeEach
public void setUp() {
- original = new ByteArrayOutputStream(){
+ original = new ByteArrayOutputStream() {
+
+ @Override
+ public void write(final byte[] ba) {
+ hit.set(true);
+ super.write(ba);
+ }
+
@Override
- public void write(final byte[] ba) throws IOException {
- if (ba != null){
- super.write(ba);
- }
+ public synchronized void write(final int ba) {
+ hit.set(true);
+ super.write(ba);
}
};
proxied = new ProxyOutputStream(original);
@@ -48,15 +59,20 @@ public void write(final byte[] ba) throws IOException {
@Test
public void testWrite() throws Exception {
+ assertFalse(hit.get());
proxied.write('y');
+ assertTrue(hit.get());
assertEquals(1, original.size());
assertEquals('y', original.toByteArray()[0]);
}
@Test
- public void testWriteNullBaSucceeds() throws Exception {
+ public void testWriteNullArrayProxiesToUnderlying() throws Exception {
+ assertFalse(hit.get());
final byte[] ba = null;
- original.write(ba);
- proxied.write(ba);
+ assertThrows(NullPointerException.class, () -> original.write(ba));
+ assertTrue(hit.get());
+ assertThrows(NullPointerException.class, () -> proxied.write(ba));
+ assertTrue(hit.get());
}
}