Created
May 30, 2010 11:24
-
-
Save k-tsj/418960 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io._ | |
import java.net._ | |
object Main { | |
trait StreamTypeInferencer[A, B] | |
object StreamTypeInferencer { | |
implicit object InputStreamInputStream | |
extends StreamTypeInferencer[InputStream, InputStream] | |
implicit object InputStreamBufferedInputStream | |
extends StreamTypeInferencer[BufferedInputStream, InputStream] | |
implicit object InputStreamReaderInputStream | |
extends StreamTypeInferencer[InputStreamReader, InputStream] | |
implicit object BufferedReaderInputStream | |
extends StreamTypeInferencer[BufferedReader, InputStream] | |
implicit object OutputStreamOutputStream | |
extends StreamTypeInferencer[OutputStream, OutputStream] | |
implicit object OutputStreamBufferedOutputStream | |
extends StreamTypeInferencer[BufferedOutputStream, OutputStream] | |
implicit object OutputStreamWriterOutputStream | |
extends StreamTypeInferencer[OutputStreamWriter, OutputStream] | |
implicit object BufferedWriterOutputStream | |
extends StreamTypeInferencer[BufferedWriter, OutputStream] | |
} | |
trait StreamConverter[A, B] { | |
def convert(stream: A): B | |
} | |
object StreamConverter { | |
implicit object InputStream2InputStream extends StreamConverter[InputStream, InputStream] { | |
override def convert(stream: InputStream): InputStream = stream | |
} | |
implicit object InputStream2BufferedInputStream extends StreamConverter[InputStream, BufferedInputStream] { | |
override def convert(stream: InputStream): BufferedInputStream = | |
new BufferedInputStream(stream) | |
} | |
implicit object InputStream2InputStreamReader extends StreamConverter[InputStream, InputStreamReader] { | |
override def convert(stream: InputStream): InputStreamReader = | |
new InputStreamReader(stream) | |
} | |
implicit object InputStream2BufferedReader extends StreamConverter[InputStream, BufferedReader] { | |
override def convert(stream: InputStream): BufferedReader = | |
new BufferedReader(InputStream2InputStreamReader.convert(stream)) | |
} | |
implicit object OutputStream2OutputStream extends StreamConverter[OutputStream, OutputStream] { | |
override def convert(stream: OutputStream): OutputStream = stream | |
} | |
implicit object OutputStream2BufferedOutputStream extends StreamConverter[OutputStream, BufferedOutputStream] { | |
override def convert(stream: OutputStream): BufferedOutputStream = | |
new BufferedOutputStream(stream) | |
} | |
implicit object OutputStream2OutputStreamWriter extends StreamConverter[OutputStream, OutputStreamWriter] { | |
override def convert(stream: OutputStream): OutputStreamWriter = | |
new OutputStreamWriter(stream) | |
} | |
implicit object OutputStream2BufferedWriter extends StreamConverter[OutputStream, BufferedWriter] { | |
override def convert(stream: OutputStream): BufferedWriter = | |
new BufferedWriter(OutputStream2OutputStreamWriter.convert(stream)) | |
} | |
} | |
trait ResourceOpener[A] { | |
def open(spec: String): A | |
} | |
trait FileOpener[A] extends ResourceOpener[A] | |
object FileOpener { | |
implicit object FileInputStreamOpener extends FileOpener[InputStream] { | |
def open(path: String): InputStream = new FileInputStream(path) | |
} | |
implicit object FileOutputStreamOpener extends FileOpener[OutputStream] { | |
def open(path: String): OutputStream = new FileOutputStream(path) | |
} | |
} | |
trait URIOpener[A] extends ResourceOpener[A] | |
object URIOpener { | |
implicit object URIInputStreamOpener extends URIOpener[InputStream] { | |
def open(url: String): InputStream = { | |
new URL(url).openConnection() match { | |
case conn: HttpURLConnection => { | |
conn.connect() | |
conn.getResponseCode match { | |
case 200 => { | |
conn.getInputStream | |
} | |
case c => error("failed to connect(%s)".format(c)) | |
} | |
} | |
} | |
} | |
} | |
} | |
trait Resource { | |
def open[A <: Closeable, B, That](path: String)(f: A => B) | |
(implicit inferencer: StreamTypeInferencer[A, That], | |
opener: ResourceOpener[That], | |
converter: StreamConverter[That, A]): B = { | |
val res: A = converter.convert(opener.open(path)) | |
try { | |
f(res) | |
} | |
finally { | |
res.close() | |
} | |
} | |
} | |
object File extends Resource { | |
def open[A <: Closeable, B, That](path: String)(f: A => B) | |
(implicit inferencer: StreamTypeInferencer[A, That], | |
opener: FileOpener[That], | |
converter: StreamConverter[That, A]): B = { | |
super.open(path)(f) | |
} | |
} | |
object OpenURI extends Resource { | |
def open[A <: Closeable, B, That](path: String)(f: A => B) | |
(implicit inferencer: StreamTypeInferencer[A, That], | |
opener: URIOpener[That], | |
converter: StreamConverter[That, A]): B = { | |
super.open(path)(f) | |
} | |
} | |
//////////////////////////////////////////////////////////////// | |
// (String)(A => B)というシグネチャにこだわらなければこっちの方が汎用的 | |
def using[A <: Closeable, B](in: InputStream)(f: A => B) | |
(implicit converter: StreamConverter[InputStream, A]): B = { | |
usingImpl(in)(f) | |
} | |
def using[A <: Closeable, B](out: OutputStream)(f: A => B) | |
(implicit converter: StreamConverter[OutputStream, A]): B = { | |
usingImpl(out)(f) | |
} | |
protected def usingImpl[A, B <: Closeable, C] | |
(basicStream: A)(f: B => C) | |
(implicit converter: StreamConverter[A, B]): C = { | |
val stream: B = converter.convert(basicStream) | |
try { | |
f(stream) | |
} | |
finally { | |
stream.close() | |
} | |
} | |
//////////////////////////////////////////////////////////////// | |
def main(args: Array[String]): Unit = { | |
File.open("foo.txt") { in: InputStream => | |
File.open("bar.txt") { out: BufferedWriter => | |
out.write(in.read) | |
} | |
} | |
File.open("foo.txt") { in: InputStreamReader => } | |
File.open("foo.txt") { in: BufferedReader => } | |
OpenURI.open("http://example.com/") { in: BufferedReader => } | |
// こっちはコンパイルエラー | |
// OpenURI.open("http://example.com/") { (out: BufferedWriter) => } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment