src/scalax/io/resources.scala
author Stepan Koltsov <yozh@mx1.ru>
Sat May 31 09:40:22 2008 +0400 (13 months ago)
changeset 136 2382da3e650f
parent 1324c5180b06e18
child 14935348dfcaec1
permissions -rw-r--r--
WriterResource.write method
        1 // -----------------------------------------------------------------------------
        2 //
        3 //  Scalax - The Scala Community Library
        4 //  Copyright (c) 2005-8 The Scalax Project. All rights reserved.
        5 //
        6 //  The primary distribution site is http://scalax.scalaforge.org/
        7 //
        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.
       10 //
       11 // -----------------------------------------------------------------------------
       12 
       13 package scalax.io
       14 import java.io._
       15 import java.util.zip._
       16 import java.net.URL
       17 import java.nio.channels._
       18 import java.nio.charset._
       19 import java.util.regex._
       20 import scalax.control._
       21 
       22 abstract class ResourceFactory {
       23 	/** Resource type */
       24 	type RT
       25 	
       26 	def file(f : File) : RT
       27 	
       28 	def file(path : String) : RT = file(new File(path))
       29 	
       30 	def url(u : String) : RT
       31 	
       32 	def apply(path : String) = {
       33 		if (path.startsWith("/") || path.startsWith("./")) file(new File(path))
       34 		else url(path)
       35 	}
       36 }
       37 
       38 abstract class CloseableResource[+C <: Closeable] extends ManagedResource[C] { self =>
       39 	type Handle <: C
       40 	final def translate(v : Handle) = v
       41 	override def unsafeClose(r : Handle) = r.close()
       42 	
       43 	protected trait Wrapper {
       44 		type Handle
       45 		type SelfHandle = self.Handle
       46 		
       47 		def wrap(is : SelfHandle) : Handle
       48 		
       49 		def unsafeOpen() = {
       50 			val is = self.unsafeOpen()
       51 			try {
       52 				wrap(is)
       53 			} catch {
       54 				case e =>
       55 					self.unsafeCloseQuietly(is)
       56 					throw e
       57 			}
       58 		}
       59 	}
       60 }
       61 
       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)
       67 			
       68 			override def buffered = this
       69 		}
       70 
       71 	def slurp() = for (is <- this) yield StreamHelp.slurp(is)
       72 	
       73 	/* Obtains a Reader using the system default charset. */
       74 	def reader =
       75 		new ReaderResource[Reader] with Wrapper {
       76 			type Handle = Reader
       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)
       80 		}
       81 	
       82 	def gunzip =
       83 		new InputStreamResource[GZIPInputStream] with Wrapper {
       84 			type Handle = GZIPInputStream
       85 			override def wrap(is : SelfHandle) = new GZIPInputStream(is)
       86 		}
       87 	
       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 {
       93 			type Handle = Reader
       94 			override def wrap(is : SelfHandle) = new InputStreamReader(is, cs)
       95 		}
       96 	}
       97 	
       98 	def lines = reader.lines
       99 	
      100 	def lines(charset : String) = reader(charset).lines
      101 	
      102 	def readLines() = reader.readLines()
      103 	
      104 	def readLine() = reader.readLine()
      105 	
      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)
      109 	}
      110 }
      111 
      112 object InputStreamResource extends ResourceFactory {
      113 	override type RT = InputStreamResource[InputStream]
      114 	
      115 	def apply[I <: InputStream](is : => I) =
      116 		new InputStreamResource[I] {
      117 			type Handle = I
      118 			def unsafeOpen() = is
      119 		}
      120 	
      121 	def bytes(array : Array[Byte], offset : Int, length : Int) =
      122 		apply(new ByteArrayInputStream(array, offset, length))
      123 	
      124 	def bytes(b : Array[Byte]) : InputStreamResource[ByteArrayInputStream] =
      125 		bytes(b, 0, b.length)
      126 	
      127 	override def file(file : File) =
      128 		apply(new FileInputStream(file))
      129 
      130 	private def url(url : java.net.URL) = {
      131 		def is = {
      132 			// see org.springframework.core.io.UrlResource
      133 			val conn = url.openConnection()
      134 			conn.setUseCaches(false)
      135 			conn.getInputStream()
      136 		}
      137 		apply(is)
      138 	}
      139 	
      140 	def classpath(path : String): InputStreamResource[InputStream] =
      141 		classpath(path, Thread.currentThread.getContextClassLoader)
      142 	
      143 	def classpath(path : String, classLoader: ClassLoader): InputStreamResource[InputStream] = {
      144 		def is = {
      145 			val is = classLoader.getResourceAsStream(path)
      146 			if (is eq null) throw new FileNotFoundException
      147 			is
      148 		}
      149 		apply(is)
      150 	}
      151 
      152 	private val CLASSPATH_URL_PREFIX = "classpath:"
      153 	
      154 	private val GZIP_URL_PREFIXES = List("gzip:", "gunzip:")
      155 	
      156 	override def url(u : String): InputStreamResource[InputStream] = {
      157 		if (u startsWith CLASSPATH_URL_PREFIX) classpath(u.substring(CLASSPATH_URL_PREFIX.length))
      158 		else {
      159 			val gzippedO = GZIP_URL_PREFIXES.map(prefix => (prefix, u startsWith prefix))
      160 					.find(_._2)
      161 					.map(_._1)
      162 			if (gzippedO.isDefined) {
      163 				url(u.substring(gzippedO.get.length)).gunzip
      164 			} else url(new URL(u))
      165 		}
      166 	}
      167 	
      168 }
      169 
      170 abstract class ReaderResource[+R <: Reader] extends CloseableResource[R] {
      171 
      172 	def slurp() = for (r <- this) yield StreamHelp.slurp(r)
      173 	
      174 	def buffered : ReaderResource[BufferedReader] =
      175 		new ReaderResource[BufferedReader] with Wrapper {
      176 			type Handle = BufferedReader
      177 			override def wrap(r : SelfHandle) = new BufferedReader(r)
      178 			
      179 			override def buffered = this
      180 		}
      181 	
      182 	def lines =
      183 		new ManagedSequence[String] {
      184 			type Handle = BufferedReader
      185 			val resource = ReaderResource.this.buffered
      186 			override def iterator(v : BufferedReader) = StreamHelp.lines(v)
      187 		}
      188 	
      189 	def readLines(): Seq[String] = lines.toList
      190 	
      191 	/** First line or <code>""</code> if file is empty */
      192 	def readLine() = lines.headOption.getOrElse("")
      193 	
      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)
      197 	}
      198 }
      199 
      200 object ReaderResource extends ResourceFactory  {
      201 	override type RT = ReaderResource[Reader]
      202 
      203 	def string(s : String) =
      204 		apply(new StringReader(s))
      205 	
      206 	def apply[R <: Reader](r : => R) =
      207 		new ReaderResource[R] {
      208 			type Handle = R
      209 			def unsafeOpen() = r
      210 		}
      211 	
      212 	override def file(f : File) =
      213 		InputStreamResource.file(f).reader
      214 	
      215 	override def url(u : String) =
      216 		InputStreamResource.url(u).reader
      217 	
      218 	def classpath(p : String) =
      219 		InputStreamResource.classpath(p).reader
      220 }
      221 
      222 abstract class OutputStreamResource[+O <: OutputStream] extends CloseableResource[O] {
      223 	
      224 	def buffered : OutputStreamResource[BufferedOutputStream] =
      225 		new OutputStreamResource[BufferedOutputStream] with Wrapper {
      226 			type Handle = BufferedOutputStream
      227 			override def wrap(os : SelfHandle) = new BufferedOutputStream(os)
      228 			
      229 			override def buffered = this
      230 		}
      231 	
      232 	// XXX: should use UTF-8, see comment above
      233 	/** Obtains a Writer using the system default charset. */
      234 	def writer =
      235 		new WriterResource[Writer] with Wrapper {
      236 			type Handle = Writer
      237 			override def wrap(os : SelfHandle) = new OutputStreamWriter(os)
      238 		}
      239 	
      240 	def gzip =
      241 		new OutputStreamResource[GZIPOutputStream] with Wrapper {
      242 			type Handle = GZIPOutputStream
      243 			override def wrap(os : SelfHandle) = new GZIPOutputStream(os)
      244 		}
      245 	
      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 {
      250 			type Handle = Writer
      251 			override def wrap(os : SelfHandle) = new OutputStreamWriter(os, cs)
      252 		}
      253 	}
      254 	
      255 	def writeLine(line : String) = writer.writeLine(line)
      256 	
      257 	def writeLines(lines : Seq[String]) = writer.writeLines(lines)
      258 	
      259 	def writeString(string : String) = writer.writeString(string)
      260 	
      261 	def pumpFrom[I <: InputStream](isr : InputStreamResource[I]) {
      262 		isr pumpTo this
      263 	}
      264 }
      265 
      266 object OutputStreamResource extends ResourceFactory {
      267 	override type RT = OutputStreamResource[OutputStream]
      268 	
      269 	def apply[O <: OutputStream](os : => O) =
      270 		new OutputStreamResource[O] {
      271 			type Handle = O
      272 			def unsafeOpen() = os
      273 		}
      274 
      275 	def fileAppend(file : File, append : Boolean) =
      276 		apply(new FileOutputStream(file, append))
      277 	
      278 	def fileAppend(file : File) : OutputStreamResource[FileOutputStream] =
      279 		fileAppend(file, true)
      280 		
      281 	override def file(file : File) =
      282 		apply(new FileOutputStream(file))
      283 		
      284 	private val FILE_URL_PREFIX = "file:"
      285 	private val GZIP_URL_PREFIX = "gzip:"
      286 	
      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)
      291 	}
      292 	
      293 }
      294 
      295 abstract class WriterResource[+W <: Writer] extends CloseableResource[W] {
      296 	
      297 	def buffered : WriterResource[BufferedWriter] =
      298 		new WriterResource[BufferedWriter] with Wrapper {
      299 			type Handle = BufferedWriter
      300 			override def wrap(w : SelfHandle) = new BufferedWriter(w)
      301 			
      302 			override def buffered = this
      303 		}
      304 	
      305 	def printWriter: WriterResource[PrintWriter] =
      306 		new WriterResource[PrintWriter] with Wrapper {
      307 			type Handle = PrintWriter
      308 			override def wrap(w : SelfHandle) = new PrintWriter(w)
      309 			
      310 			override def printWriter: WriterResource[PrintWriter] = this
      311 		}
      312 	
      313 	def writeString(string : String) {
      314 		for (w <- this) w.write(string)
      315 	}
      316 	
      317 	/** Alias for <code>writeString</code> */
      318 	def write(string : String) = writeString(string)
      319 	
      320 	/** Write strings adding line separator after each line */
      321 	def writeLines(lines : Seq[String]) {
      322 		for (w <- buffered; line <- lines) {
      323 			w.write(line)
      324 			w.write(FileHelp.lineSeparator)
      325 		}
      326 	}
      327 	
      328 	/** Write string followed by line separator */
      329 	def writeLine(line : String) {
      330 		writeLines(line :: Nil)
      331 	}
      332 	
      333 	def pumpFrom[R <: Reader](rr : ReaderResource[R]) {
      334 		rr pumpTo this
      335 	}
      336 }
      337 
      338 object WriterResource extends ResourceFactory {
      339 	override type RT = WriterResource[Writer]
      340 	
      341 	def apply[W <: Writer](w : => W) =
      342 		new WriterResource[W] {
      343 			type Handle = W
      344 			def unsafeOpen() = w
      345 		}
      346 	
      347 	override def file(f : File) =
      348 		OutputStreamResource.file(f).writer
      349 	
      350 	override def url(u : String) =
      351 		OutputStreamResource.url(u).writer
      352 }
      353 
      354 // vim: set ts=4 sw=4 noet: