From e7bdebbdf69c62404e93a3e69c6d4eba7f7fc62f Mon Sep 17 00:00:00 2001 From: Lex Spoon Date: Tue, 21 Mar 2006 14:29:49 +0000 Subject: Added a Fluid class, and used it to allow console to have new input and output streams temporarily pushed in a thread-sensible way. --- src/library/scala/Console.scala | 74 +++++++++++++++++++++++++++++++------- src/library/scala/util/Fluid.scala | 61 +++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 12 deletions(-) create mode 100644 src/library/scala/util/Fluid.scala diff --git a/src/library/scala/Console.scala b/src/library/scala/Console.scala index a3e9a5383b..79a3c586d8 100644 --- a/src/library/scala/Console.scala +++ b/src/library/scala/Console.scala @@ -10,6 +10,7 @@ package scala; +import scala.util.Fluid; /** The Console object implements functionality for @@ -52,34 +53,83 @@ object Console { final val REVERSED = "\033[7m"; final val INVISIBLE = "\033[8m"; - private var out: PrintStream = java.lang.System.out; - private var in: BufferedReader = - new BufferedReader(new InputStreamReader(java.lang.System.in)); + private val outFluid = new Fluid[PrintStream](java.lang.System.out); + private val inFluid = new Fluid[BufferedReader]( + new BufferedReader(new InputStreamReader(java.lang.System.in))); + + private def out = outFluid.value + private def in = inFluid.value /** Set the default output stream. * * @param out the new output stream. */ - def setOut(out: PrintStream): Unit = { - this.out = out; - } + def setOut(out: PrintStream): Unit = outFluid.set(out) - /** Set the default input stream. + /** Set the default output stream for the duration + * of execution of one thunk. * - * @param in the new input stream. + * @param out the new output stream. + * @param thunk the code to execute with + * the new output stream active */ - def setIn(in: InputStream): Unit = { - this.in = new BufferedReader(new InputStreamReader(in)); - } + def withOut[T](out: PrintStream)(thunk: =>T): T = + outFluid.withValue(out)(thunk) + + /** Set the default output stream. + * + * @param@ out the new output stream. + */ + def setOut(out: OutputStream): Unit = + setOut(new PrintStream(out)) + + /** Set the default output stream for the duration + * of execution of one thunk. + * + * @param out the new output stream. + * @param thunk the code to execute with + * the new output stream active + */ + def withOut[T](out: OutputStream)(thunk: =>T): T = + withOut(new PrintStream(out))(thunk) + /** Set the default input stream. * * @param reader specifies the new input stream. */ def setIn(reader: Reader): Unit = { - this.in = new BufferedReader(reader); + inFluid.set(new BufferedReader(reader)) } + /** Set the default input stream for the duration + * of execution of one thunk. + * + * @param in the new input stream. + * @param thunk the code to execute with + * the new input stream active + */ + def withIn[T](reader: Reader)(thunk: =>T): T = + inFluid.withValue(new BufferedReader(reader))(thunk) + + + /** Set the default input stream. + * + * @param in the new input stream. + */ + def setIn(in: InputStream): Unit = + setIn(new InputStreamReader(in)) + + /** Set the default input stream for the duration + * of execution of one thunk. + * + * @param in the new input stream. + * @param thunk the code to execute with + * the new input stream active + */ + def withIn[T](in: InputStream)(thunk: =>T): T = + withIn(new InputStreamReader(in))(thunk) + /** Print an object on the terminal. * * @param obj the object to print. diff --git a/src/library/scala/util/Fluid.scala b/src/library/scala/util/Fluid.scala new file mode 100644 index 0000000000..09ea91cc80 --- /dev/null +++ b/src/library/scala/util/Fluid.scala @@ -0,0 +1,61 @@ +package scala.util + + +/** Fluids provide a binding mechanism where the current + * value is found through dynamic scope, but where + * access to the fluid itself is resolved through static + * binding to a variable referencing the fluid. + * + * The current value can be retrieved with the + * value method. New values can be + * pushed using the withValue method. + * Values pushed via withValue only + * stay valid while the withValue's + * second argument, a parameterless closure, + * executes. When the second argument finishes, + * the fluid reverts to the previous value. + * + * Usage of withValue looks like this: + *
+  * someFluid.withValue(newValue) {
+  *   // ... code called in here that calls value ...
+  *   // ... will be given back the newValue ...
+  * }
+  * 
+ * + * Each thread gets its own stack of bindings. When a + * new thread is created, the fluid gets a copy of + * the stack of bindings from the parent thread, and + * from then on the bindings for the new thread + * are independent of those for the original thread. + */ +class Fluid[T](init: T) { + private val tl = new InheritableThreadLocal + tl.set(init) + + /** Retrieve the current value */ + def value: T = tl.get.asInstanceOf[T] + + + /** Set the value of the fluid while executing the specified + * thunk. + * + * @param newval The value to which to set the fluid + * @param thunk The + */ + def withValue[S](newval: T)(thunk: =>S): S = { + val oldval = value + tl.set(newval) + + try { thunk } finally { + tl.set(oldval) + } + } + + /** Change the currently bound value, discarding the old value. + * Usually withValue() gives better semantics. + */ + def set(newval: T) = { tl.set(newval) } + + override def toString: String = "Fluid(" + value +")" +} -- cgit v1.2.3