summaryrefslogtreecommitdiff
path: root/src/library/scala/io/Source.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/library/scala/io/Source.scala')
-rw-r--r--src/library/scala/io/Source.scala400
1 files changed, 400 insertions, 0 deletions
diff --git a/src/library/scala/io/Source.scala b/src/library/scala/io/Source.scala
new file mode 100644
index 0000000000..90bff94028
--- /dev/null
+++ b/src/library/scala/io/Source.scala
@@ -0,0 +1,400 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+// $Id$
+
+
+package scala.io
+
+
+import java.io.{BufferedInputStream, File, FileInputStream, InputStream,
+ PrintStream}
+import java.nio.{ByteBuffer, CharBuffer}
+import java.nio.charset.Charset
+import java.net.{URI, URL}
+
+/** This object provides convenience methods to create an iterable
+ * representation of a source file.
+ *
+ * @author Burak Emir
+ * @version 1.0, 19/08/2004
+ */
+object Source {
+
+ val DefaultBufSize = 2048
+
+ val NoReset: () => Source = () => throw new UnsupportedOperationException()
+
+ /** Creates a <code>Source</code> instance from the given array of bytes,
+ * with empty description.
+ *
+ * @param bytes ...
+ * @return the created <code>Source</code> instance.
+ */
+ def fromBytes(bytes: Array[Byte]): Source =
+ fromString(new String(bytes))
+
+ /** Creates Source from array of bytes with given encoding, with
+ * empty description.
+ *
+ * @param bytes ...
+ * @param enc ...
+ * @return ...
+ */
+ def fromBytes(bytes: Array[Byte], enc: String): Source =
+ fromString(new String(bytes, enc))
+
+ /** Creates a <code>Source</code> instance from a single character.
+ *
+ * @param c ...
+ * @return the create <code>Source</code> instance.
+ */
+ def fromChar(c: Char): Source = {
+ val it = Iterator.single(c)
+ new Source {
+ def reset() = fromChar(c)
+ val iter = it
+ }
+ }
+
+ /** creates Source from array of characters, with empty description.
+ *
+ * @param chars ...
+ * @return ...
+ */
+ def fromChars(chars: Array[Char]): Source = {
+ val it = chars.elements
+ new Source {
+ def reset() = fromChars(chars)
+ val iter = it
+ }
+ }
+
+ /** creates Source from string, with empty description.
+ *
+ * @param s ...
+ * @return ...
+ */
+ def fromString(s: String): Source = {
+ val it = s.elements
+ new Source {
+ def reset() = fromString(s)
+ val iter = it
+ }
+ }
+
+ /** creates Source from file with given name, setting its description to
+ * filename.
+ */
+ def fromFile(name: String): Source =
+ fromFile(name, util.Properties.encodingString)
+
+ /** creates Source from file with given name, using given encoding, setting
+ * its description to filename.
+ */
+ def fromFile(name: String, enc: String): Source =
+ fromFile(new File(name), enc)
+
+ /** creates <code>Source</code> from file with given file: URI
+ */
+ def fromFile(uri: URI): Source =
+ fromFile(uri, util.Properties.encodingString)
+
+ /** creates Source from file with given file: URI
+ */
+ def fromFile(uri: URI, enc: String): Source =
+ fromFile(new File(uri), enc)
+
+ /** creates Source from file, using default character encoding, setting its
+ * description to filename.
+ */
+ def fromFile(file: File): Source =
+ fromFile(file, util.Properties.encodingString, Source.DefaultBufSize)
+
+ /** same as fromFile(file, enc, Source.DefaultBufSize)
+ */
+ def fromFile(file: File, enc: String): Source =
+ fromFile(file, enc, Source.DefaultBufSize)
+
+ /** Creates Source from <code>file</code>, using given character encoding,
+ * setting its description to filename. Input is buffered in a buffer of
+ * size <code>bufferSize</code>.
+ */
+ def fromFile(file: File, enc: String, bufferSize: Int): Source = {
+ val inpStream = new FileInputStream(file)
+ val size = if (bufferSize > 0) bufferSize else Source.DefaultBufSize
+ setFileDescriptor(file,
+ BufferedSource.fromInputStream(inpStream, enc, size, { () => fromFile(file, enc, size)}))
+ }
+
+ /** This method sets the descr property of the given source to a string of the form "file:"+path
+ * @param file the file whose path we want to describe
+ * @param s the source whose property we set
+ * @return s
+ */
+ private def setFileDescriptor(file: File, s: Source): Source = {
+ s.descr = new StringBuilder("file:").append(file.getAbsolutePath()).toString();
+ s
+ }
+
+ /**
+ * @param s ...
+ * @return ...
+ * @deprecated use fromURL(s, enc)
+ */
+ def fromURL(s: String): Source =
+ fromURL(new URL(s))
+
+ /** same as fromURL(new URL(s), enc)
+ */
+ def fromURL(s: String, enc:String): Source =
+ fromURL(new URL(s), enc)
+
+ /**
+ * @param url ...
+ * @return ...
+ * @deprecated use fromURL(url, enc)
+ */
+ def fromURL(url: URL): Source = {
+ val it = new Iterator[Char] {
+ var data: Int = _
+ def hasNext = {data != -1}
+ def next = {val x = data.asInstanceOf[Char]; data = bufIn.read(); x}
+ val in = url.openStream()
+ val bufIn = new BufferedInputStream(in)
+ data = bufIn.read()
+ }
+ val s = new Source {
+ def reset() = fromURL(url)
+ val iter = it
+ }
+ s.descr = url.toString()
+ s
+ }
+
+ /** same as fromInputStream(url.openStream(), enc)
+ */
+ def fromURL(url: URL, enc:String): Source =
+ fromInputStream(url.openStream(), enc)
+
+ /** reads data from <code>istream</code> into a byte array, and calls
+ * <code>fromBytes</code> with given encoding <code>enc</code>.
+ * If <code>maxlen</code> is given, reads not more bytes than <code>maxlen</code>;
+ * if <code>maxlen</code> was not given, or <code>was &lt;= 0</code>, then
+ * whole <code>istream</code> is read and closed afterwards.
+ *
+ * @param istream the input stream from which to read
+ * @param enc the encoding to apply to the bytes
+ * @param maxlen optionally, a positive int specifying maximum number of bytes to read
+ */
+ @deprecated def fromInputStream(istream: InputStream, enc: String, maxlen: Option[Int]): Source = {
+ val limit = maxlen match { case Some(i) => i; case None => 0 }
+ val bi = new BufferedInputStream(istream, Source.DefaultBufSize)
+ val bytes = new collection.mutable.ArrayBuffer[Byte]()
+ var b = 0
+ var i = 0
+ while( {b = bi.read; i += 1; b} != -1 && (limit <= 0 || i < limit)) {
+ bytes += b.toByte;
+ }
+ if(limit <= 0) bi.close
+ fromBytes(bytes.toArray, enc)
+ }
+
+ /** same as BufferedSource.fromInputStream(is, enc, Source.DefaultBufSize)
+ */
+ def fromInputStream(is: InputStream, enc: String): Source =
+ BufferedSource.fromInputStream(is, enc, Source.DefaultBufSize, { () => fromInputStream(is, enc) })
+
+ /** same as BufferedSource.fromInputStream(is, "utf-8", Source.DefaultBufSize) */
+ def fromInputStream(is: InputStream): Source =
+ BufferedSource.fromInputStream(is, "utf-8", Source.DefaultBufSize, { () => fromInputStream(is) })
+
+}
+
+/** The class <code>Source</code> implements an iterable representation
+ * of source files. Calling method <code>reset</code> returns an identical,
+ * resetted source.
+ *
+ * @author Burak Emir
+ * @version 1.0
+ */
+abstract class Source extends Iterator[Char] {
+
+ // ------ protected values
+
+ /** the actual iterator */
+ protected val iter: Iterator[Char]
+
+ protected var cline = 1
+ protected var ccol = 1
+
+ // ------ public values
+
+ /** position of last character returned by next*/
+ var pos = 0
+
+ /** the last character returned by next.
+ * the value before the first call to next is undefined.
+ */
+ var ch: Char = _
+
+ /** description of this source, default empty */
+ var descr: String = ""
+
+ var nerrors = 0
+ var nwarnings = 0
+
+ /** default col increment for tabs '\t', set to 4 initially
+ */
+ var tabinc = 4
+
+ //
+ // -- methods
+ //
+
+ /** convenience method, returns given line (not including newline)
+ * from Source.
+ *
+ * @param line the line index, first line is 1
+ * @return the character string of the specified line.
+ * @throws scala.compat.Platform.IllegalArgumentException
+ *
+ */
+ def getLine(line: Int): String = { // faster than getLines.drop(line).next
+ // todo: should @throws scala.compat.Platform.IndexOutOfBoundsException
+ if (line < 1) throw new IllegalArgumentException(line.toString);
+ val buf = new StringBuilder()
+ val it = reset
+ var i = 0
+
+ while (it.hasNext && i < (line-1))
+ if ('\n' == it.next)
+ i += 1;
+
+ if (!it.hasNext) // this should not happen
+ throw new IllegalArgumentException(
+ "line " + line + " does not exist"
+ );
+
+ var ch = it.next
+ while (it.hasNext && '\n' != ch) {
+ buf append ch
+ ch = it.next
+ }
+
+ if ('\n' != ch)
+ buf append ch
+
+ val res = buf.toString()
+ buf setLength 0 // hopefully help collector to deallocate StringBuilder
+ res
+ }
+
+ /** returns an iterator who returns lines (including newline character).
+ * a line ends in \n.
+ */
+ def getLines: Iterator[String] = new Iterator[String] {
+ val buf = new StringBuilder
+ def next = {
+ var ch = iter.next
+ while(ch != '\n' && iter.hasNext) {
+ buf append ch
+ ch = iter.next
+ }
+ buf.append(ch)
+ val res = buf.toString()
+ buf setLength 0 // clean things up for next call of "next"
+ res
+ }
+ def hasNext = iter.hasNext
+ }
+ /** Returns <code>true</code> if this source has more characters.
+ */
+ def hasNext = iter.hasNext
+
+ /** returns next character and has the following side-effects: updates
+ * position (ccol and cline) and assigns the character to ch
+ */
+ def next = {
+ ch = iter.next
+ pos = Position.encode(cline,ccol)
+ ch match {
+ case '\n' =>
+ ccol = 1
+ cline += 1
+ case '\t' =>
+ ccol += tabinc
+ case _ =>
+ ccol += 1
+ }
+ ch
+ }
+
+ /** Reports an error message to console.
+ *
+ * @param pos ...
+ * @param msg the error message to report
+ */
+ def reportError(pos: Int, msg: String) {
+ reportError(pos, msg, java.lang.System.out)
+ }
+
+ /** Reports an error message to the output stream <code>out</code>.
+ *
+ * @param pos ...
+ * @param msg the error message to report
+ * @param out ...
+ */
+ def reportError(pos: Int, msg: String, out: PrintStream) {
+ nerrors = nerrors + 1
+ report(pos, msg, out)
+ }
+
+ /**
+ * @param pos ...
+ * @param msg the error message to report
+ * @param out ...
+ */
+ def report(pos: Int, msg: String, out: PrintStream) {
+ val buf = new StringBuilder
+ val line = Position.line(pos)
+ val col = Position.column(pos)
+ buf.append(descr + ":" + line + ":" + col + ": " + msg)
+ buf.append(getLine(line))
+ var i = 1
+ while (i < col) {
+ buf.append(' ')
+ i += 1
+ }
+ buf.append('^')
+ out.println(buf.toString)
+ }
+
+ /** Reports a warning message to <code>java.lang.System.out</code>.
+ *
+ * @param pos ...
+ * @param msg the warning message to report
+ */
+ def reportWarning(pos: Int, msg: String) {
+ reportWarning(pos, msg, java.lang.System.out)
+ }
+
+ /**
+ * @param pos ...
+ * @param msg the warning message to report
+ * @param out ...
+ */
+ def reportWarning(pos: Int, msg: String, out: PrintStream) {
+ nwarnings = nwarnings + 1
+ report(pos, "warning! " + msg, out)
+ }
+
+ /** the actual reset method */
+ def reset(): Source
+
+}