Instantly share code, notes, and snippets.

Embed
What would you like to do?
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