diff --git a/user/super/com/google/gwt/emul/java/io/ByteArrayOutputStream.java b/user/super/com/google/gwt/emul/java/io/ByteArrayOutputStream.java index ca3a166244..9e65e0771d 100644 --- a/user/super/com/google/gwt/emul/java/io/ByteArrayOutputStream.java +++ b/user/super/com/google/gwt/emul/java/io/ByteArrayOutputStream.java @@ -19,6 +19,8 @@ package java.io; +import java.nio.charset.Charset; + /** * A specialized {@link OutputStream} for class for writing content to an * (internal) byte array. As bytes are written to this stream, the byte array @@ -175,6 +177,18 @@ public String toString(String charsetName) throws UnsupportedEncodingException { return new String(buf, 0, count, charsetName); } + /** + * Returns the contents of this ByteArrayOutputStream as a string converted + * according to the encoding declared in {@code charsetName}. + * + * @param charset + * the encoding to use when translating this stream to a string. + * @return this stream's current contents as an encoded string. + */ + public String toString(Charset charset) { + return new String(buf, 0, count, charset); + } + /** * Writes {@code count} bytes from the byte array {@code buffer} starting at * offset {@code index} to this stream. @@ -203,6 +217,18 @@ public void write(byte[] buffer, int offset, int len) { this.count += len; } + /** + * Writes all bytes from the byte array {@code buffer} to this stream. + * + * @param buffer + * the buffer to be written. + * @throws NullPointerException + * if {@code buffer} is {@code null}. + */ + public void writeBytes(byte[] buffer) { + write(buffer, 0, buffer.length); + } + /** * Writes the specified byte {@code oneByte} to the OutputStream. Only the * low order byte of {@code oneByte} is written. diff --git a/user/super/com/google/gwt/emul/java/io/OutputStream.java b/user/super/com/google/gwt/emul/java/io/OutputStream.java index 9457086033..7bb1dd7262 100644 --- a/user/super/com/google/gwt/emul/java/io/OutputStream.java +++ b/user/super/com/google/gwt/emul/java/io/OutputStream.java @@ -48,7 +48,7 @@ * @see InputStream * *

The implementation provided by this class behaves as described in the Java - * API documentation except for {@link write(int)} which throws an exception of + * API documentation except for {@link #write(int)} which throws an exception of * type {@link java.lang.UnsupportedOperationException} instead of being * abstract. */ @@ -130,4 +130,29 @@ public void write(byte[] buffer, int offset, int count) throws IOException { * if an error occurs while writing to this stream. */ public abstract void write(int oneByte) throws IOException; + + public static OutputStream nullOutputStream() { + return new OutputStream() { + private boolean closed; + + @Override + public void write(int b) throws IOException { + if (closed) { + throw new IOException(); + } + } + + public void write(byte[] buffer, int offset, int count) throws IOException { + IOUtils.checkOffsetAndCount(buffer, offset, count); + if (closed) { + throw new IOException(); + } + } + + @Override + public void close() { + closed = true; + } + }; + } } diff --git a/user/super/com/google/gwt/emul/java/io/Writer.java b/user/super/com/google/gwt/emul/java/io/Writer.java index d837120dc0..3cb417a3d8 100644 --- a/user/super/com/google/gwt/emul/java/io/Writer.java +++ b/user/super/com/google/gwt/emul/java/io/Writer.java @@ -78,4 +78,54 @@ public Writer append(CharSequence csq, int start, int end) throws IOException { write(csq.subSequence(start, end).toString()); return this; } + + public static Writer nullWriter() { + return new Writer() { + private boolean closed; + + @Override + public void write(char[] buffer, int off, int len) throws IOException { + IOUtils.checkOffsetAndCount(buffer, off, len); + ensureOpen(); + } + + public void write(int oneChar) throws IOException { + ensureOpen(); + } + + public void write(String str, int offset, int count) throws IOException { + IOUtils.checkOffsetAndCount(str, offset, count); + ensureOpen(); + } + + public Writer append(CharSequence csq) throws IOException { + ensureOpen(); + return this; + } + + public Writer append(CharSequence csq, int start, int end) throws IOException { + ensureOpen(); + if (csq != null) { + IOUtils.checkOffsetAndCount(csq.toString(), start, end - start); + } + return this; + } + + @Override + public void flush() throws IOException { + ensureOpen(); + } + + @Override + public void close() { + closed = true; + } + + private void ensureOpen() throws IOException { + if (closed) { + throw new IOException(); + } + } + }; + } } diff --git a/user/test/com/google/gwt/emultest/java/io/ByteArrayOutputStreamTest.java b/user/test/com/google/gwt/emultest/java/io/ByteArrayOutputStreamTest.java index c7cced3189..b398f0ae5b 100644 --- a/user/test/com/google/gwt/emultest/java/io/ByteArrayOutputStreamTest.java +++ b/user/test/com/google/gwt/emultest/java/io/ByteArrayOutputStreamTest.java @@ -18,6 +18,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.util.Arrays; /** @@ -137,8 +138,17 @@ public void testToStringWithCharsetNameAndNonEmptyStream() throws IOException { assertEquals(expectedString, actualString); } + public void testToStringWithCharsetAndNonEmptyStream() throws IOException { + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1); + final String expectedString = "Hello"; + + outputStream.write(expectedString.getBytes(StandardCharsets.UTF_8)); + final String actualString = outputStream.toString(StandardCharsets.UTF_8); + assertEquals(expectedString, actualString); + } + public void testWriteSingleValues() throws IOException { - final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2); + outputStream = new ByteArrayOutputStream(2); assertEquals(0, outputStream.size()); for (int i = 0; i < 3; i++) { @@ -149,4 +159,15 @@ public void testWriteSingleValues() throws IOException { final byte[] actualBytes = outputStream.toByteArray(); assertTrue(Arrays.equals(expectedBytes, actualBytes)); } + + public void testWriteBytes() throws IOException { + outputStream = new ByteArrayOutputStream(2); + assertEquals(0, outputStream.size()); + outputStream.writeBytes(TEST_ARRAY); + final byte[] actualBytes = outputStream.toByteArray(); + assertTrue(Arrays.equals(TEST_ARRAY, actualBytes)); + outputStream.writeBytes(new byte[0]); + final byte[] actualBytes2 = outputStream.toByteArray(); + assertTrue(Arrays.equals(TEST_ARRAY, actualBytes)); + } } diff --git a/user/test/com/google/gwt/emultest/java/io/OutputStreamTest.java b/user/test/com/google/gwt/emultest/java/io/OutputStreamTest.java index c5f8370df2..40992d0501 100644 --- a/user/test/com/google/gwt/emultest/java/io/OutputStreamTest.java +++ b/user/test/com/google/gwt/emultest/java/io/OutputStreamTest.java @@ -62,4 +62,16 @@ public void testDefaultBehaviorOfFlush() throws IOException { // should do nothing (including not throwing an exception). outputStream.flush(); } + + public void testNullOutputStream() throws IOException { + OutputStream nullStream = OutputStream.nullOutputStream(); + nullStream.write(42); + nullStream.close(); + try { + nullStream.write(1); + fail("Writing to a closed stream should fail."); + } catch (IOException expected) { + // expected + } + } } diff --git a/user/test/com/google/gwt/emultest/java/io/WriterTest.java b/user/test/com/google/gwt/emultest/java/io/WriterTest.java index adbae10671..c8df42a38a 100644 --- a/user/test/com/google/gwt/emultest/java/io/WriterTest.java +++ b/user/test/com/google/gwt/emultest/java/io/WriterTest.java @@ -247,4 +247,34 @@ public void testWriteNonEmptySubstring() throws IOException { writer.write(str, 1, 2); assertTrue(Arrays.equals("ol".toCharArray(), writer.toCharArray())); } + + public void testNullWriter() throws IOException { + Writer nullWriter = Writer.nullWriter(); + nullWriter.write(42); + nullWriter.append('a'); + nullWriter.write("hola", 1, 2); + nullWriter.close(); + // writing to closed stream fails + assertThrows(IOException.class, () -> nullWriter.write(42)); + assertThrows(IOException.class, () -> nullWriter.append('a')); + assertThrows(IOException.class, () -> nullWriter.append("hola", 1, 2)); + // bounds check takes precedence over closed stream check + assertThrows(IndexOutOfBoundsException.class, + () -> nullWriter.write("hola", 1, 10)); + assertThrows(IndexOutOfBoundsException.class, + () -> nullWriter.write(new char[]{'h', 'o', 'l', 'a'}, 1, 10)); + } + + private void assertThrows(Class ex, Throwing toTest) { + try { + toTest.run(); + fail("should have failed"); + } catch (Exception expected) { + assertEquals(ex, expected.getClass()); + } + } + + private interface Throwing { + void run() throws Exception; + } }