summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/io/SourceReader.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/io/SourceReader.scala')
-rw-r--r--src/compiler/scala/tools/nsc/io/SourceReader.scala137
1 files changed, 137 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/io/SourceReader.scala b/src/compiler/scala/tools/nsc/io/SourceReader.scala
new file mode 100644
index 0000000000..f5be1da58a
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/io/SourceReader.scala
@@ -0,0 +1,137 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002-2006, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+
+package scala.tools.nsc.io;
+
+
+import java.io.{File, FileInputStream, IOException};
+import java.nio.{ByteBuffer, CharBuffer};
+import java.nio.channels.{FileChannel, ReadableByteChannel};
+import java.nio.charset.{CharsetDecoder, CoderResult};
+
+/** This class implements methods to read and decode source files. */
+class SourceReader(decoder: CharsetDecoder) {
+
+ import SourceReader.{decode, flush}
+
+ //########################################################################
+ // Private Fields
+
+ /** The input byte buffer (small enough to fit in cache) */
+ private val bytes: ByteBuffer = ByteBuffer.allocate(0x4000);
+
+ /** The output character buffer */
+ private var chars: CharBuffer = CharBuffer.allocate(0x4000);
+
+ //########################################################################
+ // Public Methods
+
+ /** Reads the file with the specified name. */
+ def read(filename: String): Array[Char]= read(new File(filename));
+
+ /** Reads the specified file. */
+ def read(file: File): Array[Char] = {
+ val channel: FileChannel = new FileInputStream(file).getChannel();
+ try {
+ read(channel);
+ } finally {
+ channel.close();
+ }
+ }
+
+ /** Reads the specified file. */
+ def read(file: AbstractFile): Array[Char] = {
+ val decoder: CharsetDecoder = this.decoder.reset();
+ val bytes: ByteBuffer = ByteBuffer.wrap(file.read);
+ val chars: CharBuffer = this.chars; chars.clear();
+ terminate(flush(decoder, decode(decoder, bytes, chars, true)));
+ }
+
+ /** Reads the specified byte channel. */
+ def read(input: ReadableByteChannel): Array[Char] = {
+ val decoder: CharsetDecoder = this.decoder.reset();
+ val bytes: ByteBuffer = this.bytes; bytes.clear();
+ var chars: CharBuffer = this.chars; chars.clear();
+ var endOfInput: Boolean = false
+ while (!endOfInput ) {
+ endOfInput = input.read(bytes) < 0;
+ bytes.flip();
+ chars = decode(decoder, bytes, chars, endOfInput);
+ }
+ terminate(flush(decoder, chars));
+ }
+
+ //########################################################################
+ // Private Methods
+
+ /**
+ * Sets the specified char buffer as the new output buffer and
+ * reads and returns its content.
+ */
+ private def terminate(chars: CharBuffer): Array[Char] = {
+ val result = new Array[Char](chars.length());
+ chars.get(result);
+ this.chars = chars;
+ result;
+ }
+
+}
+
+object SourceReader {
+
+ /**
+ * Decodes the content of the specified byte buffer with the
+ * specified decoder into the specified char buffer, allocating
+ * bigger ones if necessary, then compacts the byte buffer and
+ * returns the last allocated char buffer. The "endOfInput"
+ * argument indicates whether the byte buffer contains the last
+ * chunk of the input file.
+ */
+ def decode(decoder: CharsetDecoder, bytes: ByteBuffer, chars: CharBuffer,
+ endOfInput: boolean): CharBuffer =
+ {
+ val result: CoderResult = decoder.decode(bytes, chars, endOfInput);
+ if (result.isUnderflow()) {
+ bytes.compact();
+ chars;
+ } else {
+ if (result.isError()) throw new IOException(result.toString());
+ assert(result.isOverflow());
+ decode(decoder, bytes, increaseCapacity(chars), endOfInput);
+ }
+ }
+
+ /**
+ * Flushes the specified decoder into the specified char buffer,
+ * allocating bigger ones if necessary and then flips and returns
+ * the last allocated char buffer.
+ */
+ def flush(decoder: CharsetDecoder, chars: CharBuffer): CharBuffer = {
+ val result: CoderResult = decoder.flush(chars);
+ if (result.isUnderflow()) {
+ chars.flip();
+ chars;
+ } else {
+ if (result.isError()) throw new IOException(result.toString());
+ assert(result.isOverflow());
+ flush(decoder, increaseCapacity(chars));
+ }
+ }
+
+ /**
+ * Flips the specified buffer and returns a new one with the same
+ * content but with an increased capacity.
+ */
+ private def increaseCapacity(buffer: CharBuffer): CharBuffer = {
+ buffer.flip();
+ val capacity = 2 * buffer.capacity();
+ CharBuffer.allocate(capacity).put(buffer);
+ }
+
+}