diff --git a/javalib/src/main/scala/java/io/BufferedWriter.scala b/javalib/src/main/scala/java/io/BufferedWriter.scala new file mode 100644 index 0000000000..7d767751a4 --- /dev/null +++ b/javalib/src/main/scala/java/io/BufferedWriter.scala @@ -0,0 +1,77 @@ +// Ported from Scala-native + +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package java.io + +class BufferedWriter(out: Writer, sz: Int) extends Writer { + + if (sz <= 0) throw new IllegalArgumentException("Buffer size <= 0") + + def this(out: Writer) = this(out, 4096) + + private[this] val buffer: Array[Char] = new Array[Char](sz) + private[this] var pos: Int = 0 + private[this] var closed: Boolean = false + + def close(): Unit = if (!closed) { + flush() + out.close() + closed = true + } + + def flush(): Unit = { + ensureOpen() + if (pos > 0) { + out.write(buffer, 0, pos) + out.flush() + pos = 0 + } + } + + def newLine(): Unit = + write(System.lineSeparator()) + + override def write(c: Int): Unit = { + buffer.update(pos, c.toChar) + pos += 1 + if (pos == sz) + flush() + } + + override def write(s: String, off: Int, len: Int): Unit = + write(s.toCharArray, off, len) + + def write(cbuf: Array[Char], off: Int, len: Int): Unit = { + ensureOpen() + + val cbufLength = cbuf.length + if (off < 0 || off + len > cbufLength) + throw new IndexOutOfBoundsException(s"Range [${off}, ${off + len}) out of bounds for length $cbufLength") + + if (len > 0 && off + len > 0) { + val available = sz - pos + if (available > len) { + System.arraycopy(cbuf, off, buffer, pos, len) + pos += len + } else { + flush() + out.write(cbuf, off, len) + out.flush() + } + } + } + + private def ensureOpen(): Unit = + if (closed) throw new IOException("Stream closed") +} diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/BufferedWriterTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/BufferedWriterTest.scala new file mode 100644 index 0000000000..7f6f5baded --- /dev/null +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/BufferedWriterTest.scala @@ -0,0 +1,126 @@ +// Ported from Scala-native + +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.testsuite.javalib.io + +import java.io._ + +import org.junit.Ignore +import org.junit.Test +import org.junit.Assert._ + +import org.scalajs.testsuite.utils.AssertThrows.assertThrows + +class BufferedWriterTest { + + @Test def creatingBufferedWriterWithBufferSizeZeroThrowsException(): Unit = { + val writer = new OutputStreamWriter(new ByteArrayOutputStream) + assertThrows( + classOf[IllegalArgumentException], + new BufferedWriter(writer, -1) + ) + } + + @Test def canWriteSmallChunksToBufferedWriter(): Unit = { + val stringWriter = new StringWriter() + val writer = new BufferedWriter(stringWriter) + val string = "Hello, world!" + writer.write(string) + assertTrue(stringWriter.toString == "") + writer.flush() + assertTrue(stringWriter.toString == string) + } + + @Test def canWriteChunkLargerThanBufferSizeToBufferedWriter(): Unit = { + val stringWriter = new StringWriter() + val writer = new BufferedWriter(stringWriter, 1) + val string = "Hello, world!" + writer.write(string) + assertTrue(stringWriter.toString == string) + writer.flush() + assertTrue(stringWriter.toString == string) + } + + @Test def canWriteInMultiplePartsToBufferedWriter(): Unit = { + val stringWriter = new StringWriter() + val writer = new BufferedWriter(stringWriter, 5) + writer.write("Hello,") + assertTrue(stringWriter.toString == "Hello,") + writer.write(" ") + assertTrue(stringWriter.toString == "Hello,") + writer.flush() + assertTrue(stringWriter.toString == "Hello, ") + writer.write("w") + assertTrue(stringWriter.toString == "Hello, ") + writer.write("orld!") + assertTrue(stringWriter.toString == "Hello, world!") + } + + @Test def writeNegativeLengthWritesNothing(): Unit = { + val stringWriter = new StringWriter() + val writer = new BufferedWriter(stringWriter) + val string = "Hello, world!" + writer.write(string, 0, -1) + writer.flush() + assertTrue(stringWriter.toString == "") + } + + @Ignore @Test def writeNegativeLengthThrowsIndexOutOfBound(): Unit = { + val stringWriter = new StringWriter() + val writer = new BufferedWriter(stringWriter) + val string = "Hello, world!" + assertThrows(classOf[IndexOutOfBoundsException], writer.write(string, 0, -1)) + } + + @Ignore @Test def writeNegativeOffsetPlusLengthWritesNothing(): Unit = { + val stringWriter = new StringWriter() + val writer = new BufferedWriter(stringWriter) + val string = "Hello, world!" + writer.write(string, -2, 1) + writer.flush() + assertTrue(stringWriter.toString == "") + } + + @Test def writeNegativeOffsetPlusLengthThrowsIndexOutOfBound(): Unit = { + val stringWriter = new StringWriter() + val writer = new BufferedWriter(stringWriter) + val string = "Hello, world!" + assertThrows(classOf[IndexOutOfBoundsException], writer.write(string, -2, 1)) + } + + @Test def closedWritersThrow(): Unit = { + val stream = new ByteArrayOutputStream + val writer = new BufferedWriter(new OutputStreamWriter(stream)) + writer.close() + assertThrows(classOf[IOException], writer.write("Hello, world!")) + } + + @Test def closingTwiceIsHarmless(): Unit = { + val stream = new ByteArrayOutputStream + val writer = new BufferedWriter(new OutputStreamWriter(stream)) + writer.close() + writer.close() + } + + @Test def canWriteNewLineSeparator(): Unit = { + val stringWriter = new StringWriter() + val writer = new BufferedWriter(stringWriter) + writer.write("Hello") + writer.newLine() + writer.write("world!") + writer.flush() + val expectedResult = "Hello" + System.lineSeparator() + "world!" + assertTrue(stringWriter.toString == expectedResult) + } +}