From 0ef9dbcef0a6a77388dd5bce78282b7892192019 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 1 Sep 2009 15:52:47 +0000 Subject: Factored the InputStream-based implementations ... Factored the InputStream-based implementations out of File into scala.io.Streamable, where they will eventually be reused by Source. --- src/dotnet-library/scala/io/Streamable.scala | 1 + src/library/scala/io/File.scala | 71 ++--------------- src/library/scala/io/Path.scala | 2 +- src/library/scala/io/Streamable.scala | 110 +++++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 65 deletions(-) create mode 100644 src/dotnet-library/scala/io/Streamable.scala create mode 100644 src/library/scala/io/Streamable.scala diff --git a/src/dotnet-library/scala/io/Streamable.scala b/src/dotnet-library/scala/io/Streamable.scala new file mode 100644 index 0000000000..ff90ea3bbb --- /dev/null +++ b/src/dotnet-library/scala/io/Streamable.scala @@ -0,0 +1 @@ +/* Streamable.scala does not exist for the dotnet target */ \ No newline at end of file diff --git a/src/library/scala/io/File.scala b/src/library/scala/io/File.scala index f4683820d8..0b070868c1 100644 --- a/src/library/scala/io/File.scala +++ b/src/library/scala/io/File.scala @@ -18,10 +18,9 @@ import collection.Traversable object File { - def apply(path: Path)(implicit codec: Codec = null) = { - val res = path.toFile - if (codec == null) res else res withCodec codec - } + def apply(path: Path)(implicit codec: Codec = null) = + if (codec != null) new File(path.jfile)(codec) + else path.toFile // Create a temporary file def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null) = @@ -40,74 +39,22 @@ import Path._ */ class File(jfile: JFile)(implicit val creationCodec: Codec = null) extends Path(jfile) +with Streamable.Chars { - private def getCodec(): Codec = - if (creationCodec == null) Codec.default else creationCodec - - /** For explicitly setting the creation codec if necessary. - */ - def withCodec(codec: Codec) = new File(jfile)(codec) - + def withCodec(codec: Codec): File = new File(jfile)(codec) override def toDirectory: Directory = new Directory(jfile) override def toFile: File = this + override def create(): Boolean = jfile.createNewFile() override def isValid = jfile.isFile() || !jfile.exists() - - /** Convenience functions for iterating over the bytes in a file. - */ - def bytesAsInts(): Iterator[Int] = { - val in = bufferedInput() - Iterator continually in.read() takeWhile (_ != -1) - } - /** This one is intended as the fast way. - */ - def toByteArray(): Array[Byte] = { - val arr = new Array[Byte](length.toInt) - val len = arr.length - lazy val in = bufferedInput() - var offset = 0 - - try { - def loop() { - if (offset < len) { - val read = in.read(arr, offset, len - offset) - if (read >= 0) { - offset += read - loop() - } - } - } - loop() - - if (offset == arr.length) arr - else fail("Could not read entire file '%s' (%d of %d bytes)".format(name, offset, len)) - } - finally in.close() - } - def bytes(): Iterator[Byte] = bytesAsInts() map (_.toByte) - def chars(codec: Codec = getCodec()) = (Source fromFile jfile)(codec) - - /** Convenience function for iterating over the lines in the file. - */ - def lines(codec: Codec = getCodec()): Iterator[String] = chars(codec).getLines() - - /** Convenience function to import entire file into a String. - */ - def slurp(codec: Codec = getCodec()) = chars(codec).mkString + override def length = super[Path].length /** Obtains an InputStream. */ def inputStream() = new FileInputStream(jfile) - def bufferedInput() = new BufferedInputStream(inputStream()) /** Obtains a OutputStream. */ def outputStream(append: Boolean = false) = new FileOutputStream(jfile, append) def bufferedOutput(append: Boolean = false) = new BufferedOutputStream(outputStream(append)) - // def channel(append: Boolean = false) = outputStream(append).getChannel() - - /** Obtains an InputStreamReader wrapped around a FileInputStream. - */ - def reader(codec: Codec = getCodec()) = - new InputStreamReader(inputStream, codec.charSet) /** Obtains an OutputStreamWriter wrapped around a FileOutputStream. * This should behave like a less broken version of java.io.FileWriter, @@ -116,10 +63,6 @@ extends Path(jfile) def writer(append: Boolean = false, codec: Codec = getCodec()) = new OutputStreamWriter(outputStream(append), codec.charSet) - /** Wraps a BufferedReader around the result of reader(). - */ - def bufferedReader(codec: Codec = getCodec()) = new BufferedReader(reader(codec)) - /** Wraps a BufferedWriter around the result of writer(). */ def bufferedWriter(append: Boolean = false, codec: Codec = getCodec()) = diff --git a/src/library/scala/io/Path.scala b/src/library/scala/io/Path.scala index 319cdb9898..d5fde5d410 100644 --- a/src/library/scala/io/Path.scala +++ b/src/library/scala/io/Path.scala @@ -34,7 +34,7 @@ import util.Random.nextASCIIString object Path { - // not sure whether this will be problematic + // not certain these won't be problematic, but looks good so far implicit def string2path(s: String): Path = apply(s) implicit def jfile2path(jfile: JFile): Path = apply(jfile) diff --git a/src/library/scala/io/Streamable.scala b/src/library/scala/io/Streamable.scala new file mode 100644 index 0000000000..b35b40fe04 --- /dev/null +++ b/src/library/scala/io/Streamable.scala @@ -0,0 +1,110 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.io + +import java.io.{ BufferedInputStream, InputStream, PrintStream, File => JFile } +import java.io.{ BufferedReader, InputStreamReader } +import java.net.{ URI, URL } + +import collection.mutable.ArrayBuffer +import Path.fail + +/** Traits for objects which can be represented as Streams. + * + * @author Paul Phillips + * @since 2.8 + */ + +object Streamable +{ + /** Traits which can be viewed as a sequence of bytes. Source types + * which know their length should override def length: Long for more + * efficient method implementations. + */ + trait Bytes { + def inputStream(): InputStream + def length: Long = -1 + + def bufferedInput() = new BufferedInputStream(inputStream()) + def bytes(): Iterator[Byte] = bytesAsInts() map (_.toByte) + def bytesAsInts(): Iterator[Int] = { + val in = bufferedInput() + Iterator continually in.read() takeWhile (_ != -1) + } + + /** This method aspires to be the fastest way to read + * a stream of known length into memory. + */ + def toByteArray(): Array[Byte] = { + // if we don't know the length, fall back on relative inefficiency + if (length == -1L) + return (new ArrayBuffer[Byte]() ++ bytes()).toArray + + val arr = new Array[Byte](length.toInt) + val len = arr.length + lazy val in = bufferedInput() + var offset = 0 + + def loop() { + if (offset < len) { + val read = in.read(arr, offset, len - offset) + if (read >= 0) { + offset += read + loop() + } + } + } + try loop() + finally in.close() + + if (offset == arr.length) arr + else fail("Could not read entire source (%d of %d bytes)".format(offset, len)) + } + } + + /** For objects which can be viewed as Chars. The abstract creationCodec + * can safely be defined as null and will subsequently be ignored. + */ + trait Chars extends Bytes { + def creationCodec: Codec + private def failNoCodec() = fail("This method requires a Codec to be chosen explicitly.") + + /** The general algorithm for any call to a method involving byte<->char + * transformations is: if a codec is supplied (explicitly or implicitly), + * use that; otherwise if a codec was defined when the object was created, + * use that; otherwise, use Codec.default. + * + * Note that getCodec takes a codec argument rather than having methods + * always default to getCodec() and use the argument otherwise, so that + * method implementations can, where desired, identify the case where no + * codec was ever explicitly supplied. If allowDefault = false, an + * exception will be thrown rather than falling back on Codec.default. + */ + def getCodec(givenCodec: Codec = null, allowDefault: Boolean = true) = + if (givenCodec != null) givenCodec + else if (creationCodec != null) creationCodec + else if (allowDefault) Codec.default + else failNoCodec() + + def chars(codec: Codec = getCodec()): Source = (Source fromInputStream inputStream())(codec) + def lines(codec: Codec = getCodec()): Iterator[String] = chars(codec).getLines() + + /** Obtains an InputStreamReader wrapped around a FileInputStream. + */ + def reader(codec: Codec = getCodec()) = new InputStreamReader(inputStream, codec.charSet) + + /** Wraps a BufferedReader around the result of reader(). + */ + def bufferedReader(codec: Codec = getCodec()) = new BufferedReader(reader(codec)) + + /** Convenience function to import entire file into a String. + */ + def slurp(codec: Codec = getCodec()) = chars(codec).mkString + } +} \ No newline at end of file -- cgit v1.2.3