summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLex Spoon <lex@lexspoon.org>2006-03-21 14:29:49 +0000
committerLex Spoon <lex@lexspoon.org>2006-03-21 14:29:49 +0000
commite7bdebbdf69c62404e93a3e69c6d4eba7f7fc62f (patch)
tree44bfba0c71686f46a850a34ea818517daaa62b37
parent5c15a9a9d57409632582a6ffae350c636eaa720a (diff)
downloadscala-e7bdebbdf69c62404e93a3e69c6d4eba7f7fc62f.tar.gz
scala-e7bdebbdf69c62404e93a3e69c6d4eba7f7fc62f.tar.bz2
scala-e7bdebbdf69c62404e93a3e69c6d4eba7f7fc62f.zip
Added a Fluid class, and used it to allow console
to have new input and output streams temporarily pushed in a thread-sensible way.
-rw-r--r--src/library/scala/Console.scala74
-rw-r--r--src/library/scala/util/Fluid.scala61
2 files changed, 123 insertions, 12 deletions
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 <code>Console</code> 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 <em>dynamic scope</em>, but where
+ * access to the fluid itself is resolved through </em>static
+ * binding</em> to a variable referencing the fluid.
+ *
+ * The current value can be retrieved with the
+ * <code>value</code> method. New values can be
+ * pushed using the <code>withValue</code> method.
+ * Values pushed via <code>withValue</code> only
+ * stay valid while the <code>withValue</code>'s
+ * <em>second</em> argument, a parameterless closure,
+ * executes. When the second argument finishes,
+ * the fluid reverts to the previous value.
+ *
+ * Usage of <code>withValue</code> looks like this:
+ * <blockquote><pre>
+ * someFluid.withValue(newValue) {
+ * // ... code called in here that calls value ...
+ * // ... will be given back the newValue ...
+ * }
+ * </pre></blockquote>
+ *
+ * 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 +")"
+}