1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
/* NSC -- new Scala compiler
* Copyright 2005-2009 LAMP/EPFL
*/
package scala.tools.nsc
package io
import java.net.{ URI, URL }
import java.io.{ BufferedInputStream, InputStream, PrintStream, File => JFile }
import java.io.{ BufferedReader, InputStreamReader }
import scala.io.{ Codec, Source }
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
}
}
|