1 // -----------------------------------------------------------------------------
3 // Scalax - The Scala Community Library
4 // Copyright (c) 2005-8 The Scalax Project. All rights reserved.
6 // The primary distribution site is http://scalax.scalaforge.org/
8 // This software is released under the terms of the Revised BSD License.
9 // There is NO WARRANTY. See the file LICENSE for the full text.
11 // -----------------------------------------------------------------------------
15 import java.util.zip._
17 import java.nio.channels._
18 import java.nio.charset._
19 import java.util.regex._
20 import scalax.control._
22 abstract class ResourceFactory {
26 def file(f : File) : RT
28 def file(path : String) : RT = file(new File(path))
30 def url(u : String) : RT
32 def apply(path : String) = {
33 if (path.startsWith("/") || path.startsWith("./")) file(new File(path))
38 abstract class CloseableResource[+C <: Closeable] extends ManagedResource[C] { self =>
40 final def translate(v : Handle) = v
41 override def unsafeClose(r : Handle) = r.close()
43 protected trait Wrapper {
45 type SelfHandle = self.Handle
47 def wrap(is : SelfHandle) : Handle
50 val is = self.unsafeOpen()
55 self.unsafeCloseQuietly(is)
62 abstract class InputStreamResource[+I <: InputStream] extends CloseableResource[I] {
63 def buffered : InputStreamResource[BufferedInputStream] =
64 new InputStreamResource[BufferedInputStream] with Wrapper {
65 type Handle = BufferedInputStream
66 override def wrap(is : SelfHandle) = new BufferedInputStream(is)
68 override def buffered = this
71 def slurp() = for (is <- this) yield StreamHelp.slurp(is)
73 /* Obtains a Reader using the system default charset. */
75 new ReaderResource[Reader] with Wrapper {
77 // XXX: should be UTF-8 by default instead of OS default
78 // practically, here in Russia I never used default charset
79 override def wrap(is : SelfHandle) = new InputStreamReader(is)
83 new InputStreamResource[GZIPInputStream] with Wrapper {
84 type Handle = GZIPInputStream
85 override def wrap(is : SelfHandle) = new GZIPInputStream(is)
88 /** Obtains a Reader using the supplied charset. */
89 def reader(charset : String) = {
90 // Do this lookup before opening the file, since it might fail.
91 val cs = Charset.forName(charset)
92 new ReaderResource[Reader] with Wrapper {
94 override def wrap(is : SelfHandle) = new InputStreamReader(is, cs)
98 def lines = reader.lines
100 def lines(charset : String) = reader(charset).lines
102 def readLines() = reader.readLines()
104 def readLine() = reader.readLine()
106 def pumpTo[O <: OutputStream](osr : OutputStreamResource[O]) {
107 // Note InputStream should be opened before OutputStream
108 for (is <- this; os <- osr) StreamHelp.pump(is, os)
112 object InputStreamResource extends ResourceFactory {
113 override type RT = InputStreamResource[InputStream]
115 def apply[I <: InputStream](is : => I) =
116 new InputStreamResource[I] {
118 def unsafeOpen() = is
121 def bytes(array : Array[Byte], offset : Int, length : Int) =
122 apply(new ByteArrayInputStream(array, offset, length))
124 def bytes(b : Array[Byte]) : InputStreamResource[ByteArrayInputStream] =
125 bytes(b, 0, b.length)
127 override def file(file : File) =
128 apply(new FileInputStream(file))
130 private def url(url : java.net.URL) = {
132 // see org.springframework.core.io.UrlResource
133 val conn = url.openConnection()
134 conn.setUseCaches(false)
135 conn.getInputStream()
140 def classpath(path : String): InputStreamResource[InputStream] =
141 classpath(path, Thread.currentThread.getContextClassLoader)
143 def classpath(path : String, classLoader: ClassLoader): InputStreamResource[InputStream] = {
145 val is = classLoader.getResourceAsStream(path)
146 if (is eq null) throw new FileNotFoundException
152 private val CLASSPATH_URL_PREFIX = "classpath:"
154 private val GZIP_URL_PREFIXES = List("gzip:", "gunzip:")
156 override def url(u : String): InputStreamResource[InputStream] = {
157 if (u startsWith CLASSPATH_URL_PREFIX) classpath(u.substring(CLASSPATH_URL_PREFIX.length))
159 val gzippedO = GZIP_URL_PREFIXES.map(prefix => (prefix, u startsWith prefix))
162 if (gzippedO.isDefined) {
163 url(u.substring(gzippedO.get.length)).gunzip
164 } else url(new URL(u))
170 abstract class ReaderResource[+R <: Reader] extends CloseableResource[R] {
172 def slurp() = for (r <- this) yield StreamHelp.slurp(r)
174 def buffered : ReaderResource[BufferedReader] =
175 new ReaderResource[BufferedReader] with Wrapper {
176 type Handle = BufferedReader
177 override def wrap(r : SelfHandle) = new BufferedReader(r)
179 override def buffered = this
183 new ManagedSequence[String] {
184 type Handle = BufferedReader
185 val resource = ReaderResource.this.buffered
186 override def iterator(v : BufferedReader) = StreamHelp.lines(v)
189 def readLines(): Seq[String] = lines.toList
191 /** First line or <code>""</code> if file is empty */
192 def readLine() = lines.headOption.getOrElse("")
194 def pumpTo[W <: Writer](wr : WriterResource[W]) {
195 // Note Reader should be opened before Writer
196 for (r <- this; w <- wr) StreamHelp.pump(r, w)
200 object ReaderResource extends ResourceFactory {
201 override type RT = ReaderResource[Reader]
203 def string(s : String) =
204 apply(new StringReader(s))
206 def apply[R <: Reader](r : => R) =
207 new ReaderResource[R] {
212 override def file(f : File) =
213 InputStreamResource.file(f).reader
215 override def url(u : String) =
216 InputStreamResource.url(u).reader
218 def classpath(p : String) =
219 InputStreamResource.classpath(p).reader
222 abstract class OutputStreamResource[+O <: OutputStream] extends CloseableResource[O] {
224 def buffered : OutputStreamResource[BufferedOutputStream] =
225 new OutputStreamResource[BufferedOutputStream] with Wrapper {
226 type Handle = BufferedOutputStream
227 override def wrap(os : SelfHandle) = new BufferedOutputStream(os)
229 override def buffered = this
232 // XXX: should use UTF-8, see comment above
233 /** Obtains a Writer using the system default charset. */
235 new WriterResource[Writer] with Wrapper {
237 override def wrap(os : SelfHandle) = new OutputStreamWriter(os)
241 new OutputStreamResource[GZIPOutputStream] with Wrapper {
242 type Handle = GZIPOutputStream
243 override def wrap(os : SelfHandle) = new GZIPOutputStream(os)
246 /** Obtains a Writer using the supplied charset. */
247 def writer(charset : String) = {
248 val cs = Charset.forName(charset)
249 new WriterResource[Writer] with Wrapper {
251 override def wrap(os : SelfHandle) = new OutputStreamWriter(os, cs)
255 def writeLine(line : String) = writer.writeLine(line)
257 def writeLines(lines : Seq[String]) = writer.writeLines(lines)
259 def writeString(string : String) = writer.writeString(string)
261 def pumpFrom[I <: InputStream](isr : InputStreamResource[I]) {
266 object OutputStreamResource extends ResourceFactory {
267 override type RT = OutputStreamResource[OutputStream]
269 def apply[O <: OutputStream](os : => O) =
270 new OutputStreamResource[O] {
272 def unsafeOpen() = os
275 def fileAppend(file : File, append : Boolean) =
276 apply(new FileOutputStream(file, append))
278 def fileAppend(file : File) : OutputStreamResource[FileOutputStream] =
279 fileAppend(file, true)
281 override def file(file : File) =
282 apply(new FileOutputStream(file))
284 private val FILE_URL_PREFIX = "file:"
285 private val GZIP_URL_PREFIX = "gzip:"
287 override def url(u : String) : OutputStreamResource[OutputStream] = {
288 if (u startsWith FILE_URL_PREFIX) file(new File(u substring FILE_URL_PREFIX.length))
289 else if (u startsWith GZIP_URL_PREFIX) url(u substring GZIP_URL_PREFIX.length).gzip
290 else throw new IllegalArgumentException("unknown url: " + u)
295 abstract class WriterResource[+W <: Writer] extends CloseableResource[W] {
297 def buffered : WriterResource[BufferedWriter] =
298 new WriterResource[BufferedWriter] with Wrapper {
299 type Handle = BufferedWriter
300 override def wrap(w : SelfHandle) = new BufferedWriter(w)
302 override def buffered = this
305 def printWriter: WriterResource[PrintWriter] =
306 new WriterResource[PrintWriter] with Wrapper {
307 type Handle = PrintWriter
308 override def wrap(w : SelfHandle) = new PrintWriter(w)
310 override def printWriter: WriterResource[PrintWriter] = this
313 def writeString(string : String) {
314 for (w <- this) w.write(string)
317 /** Alias for <code>writeString</code> */
318 def write(string : String) = writeString(string)
320 /** Write strings adding line separator after each line */
321 def writeLines(lines : Seq[String]) {
322 for (w <- buffered; line <- lines) {
324 w.write(FileHelp.lineSeparator)
328 /** Write string followed by line separator */
329 def writeLine(line : String) {
330 writeLines(line :: Nil)
333 def pumpFrom[R <: Reader](rr : ReaderResource[R]) {
338 object WriterResource extends ResourceFactory {
339 override type RT = WriterResource[Writer]
341 def apply[W <: Writer](w : => W) =
342 new WriterResource[W] {
347 override def file(f : File) =
348 OutputStreamResource.file(f).writer
350 override def url(u : String) =
351 OutputStreamResource.url(u).writer
354 // vim: set ts=4 sw=4 noet: