/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala import java.io.{BufferedReader, InputStream, InputStreamReader, IOException, OutputStream, PrintStream, Reader} import java.text.MessageFormat import scala.util.DynamicVariable /** Implements functionality for * printing Scala values on the terminal as well as reading specific values. * Also defines constants for marking up text on ANSI terminals. * * @author Matthias Zenger * @version 1.0, 03/09/2003 */ object Console { /** Foreground color for ANSI black */ final val BLACK = "\033[30m" /** Foreground color for ANSI red */ final val RED = "\033[31m" /** Foreground color for ANSI green */ final val GREEN = "\033[32m" /** Foreground color for ANSI yellow */ final val YELLOW = "\033[33m" /** Foreground color for ANSI blue */ final val BLUE = "\033[34m" /** Foreground color for ANSI magenta */ final val MAGENTA = "\033[35m" /** Foreground color for ANSI cyan */ final val CYAN = "\033[36m" /** Foreground color for ANSI white */ final val WHITE = "\033[37m" /** Background color for ANSI black */ final val BLACK_B = "\033[40m" /** Background color for ANSI red */ final val RED_B = "\033[41m" /** Background color for ANSI green */ final val GREEN_B = "\033[42m" /** Background color for ANSI yellow */ final val YELLOW_B = "\033[43m" /** Background color for ANSI blue */ final val BLUE_B = "\033[44m" /** Background color for ANSI magenta */ final val MAGENTA_B = "\033[45m" /** Background color for ANSI cyan */ final val CYAN_B = "\033[46m" /** Background color for ANSI white */ final val WHITE_B = "\033[47m" /** Reset ANSI styles */ final val RESET = "\033[0m" /** ANSI bold */ final val BOLD = "\033[1m" /** ANSI underlines */ final val UNDERLINED = "\033[4m" /** ANSI blink */ final val BLINK = "\033[5m" /** ANSI reversed */ final val REVERSED = "\033[7m" /** ANSI invisible */ final val INVISIBLE = "\033[8m" private val outVar = new DynamicVariable[PrintStream](java.lang.System.out) private val errVar = new DynamicVariable[PrintStream](java.lang.System.err) private val inVar = new DynamicVariable[BufferedReader]( new BufferedReader(new InputStreamReader(java.lang.System.in))) /** The default output, can be overridden by `setOut` */ def out = outVar.value /** The default error, can be overridden by `setErr` */ def err = errVar.value /** The default input, can be overridden by `setIn` */ def in = inVar.value /** Sets the default output stream. * * @param out the new output stream. */ def setOut(out: PrintStream) { outVar.value = out } /** Sets the default output stream for the duration * of execution of one thunk. * * @example {{{ * withOut(Console.err) { println("This goes to default _error_") } * }}} * * @param out the new output stream. * @param thunk the code to execute with * the new output stream active * @return the results of `thunk` * @see `withOut[T](out:OutputStream)(thunk: => T)` */ def withOut[T](out: PrintStream)(thunk: =>T): T = outVar.withValue(out)(thunk) /** Sets the default output stream. * * @param out the new output stream. */ def setOut(out: OutputStream): Unit = setOut(new PrintStream(out)) /** Sets 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 * @return the results of `thunk` * @see `withOut[T](out:PrintStream)(thunk: => T)` */ def withOut[T](out: OutputStream)(thunk: =>T): T = withOut(new PrintStream(out))(thunk) /** Sets the default error stream. * * @param err the new error stream. */ def setErr(err: PrintStream) { errVar.value = err } /** Set the default error stream for the duration * of execution of one thunk. * @example {{{ * withErr(Console.out) { println("This goes to default _out_") } * }}} * * @param err the new error stream. * @param thunk the code to execute with * the new error stream active * @return the results of `thunk` * @see `withErr[T](err:OutputStream)(thunk: =>T)` */ def withErr[T](err: PrintStream)(thunk: =>T): T = errVar.withValue(err)(thunk) /** Sets the default error stream. * * @param err the new error stream. */ def setErr(err: OutputStream): Unit = setErr(new PrintStream(err)) /** Sets the default error stream for the duration * of execution of one thunk. * * @param err the new error stream. * @param thunk the code to execute with * the new error stream active * @return the results of `thunk` * @see `withErr[T](err:PrintStream)(thunk: =>T)` */ def withErr[T](err: OutputStream)(thunk: =>T): T = withErr(new PrintStream(err))(thunk) /** Sets the default input stream. * * @param reader specifies the new input stream. */ def setIn(reader: Reader) { inVar.value = new BufferedReader(reader) } /** Sets the default input stream for the duration * of execution of one thunk. * * @example {{{ * val someFile:Reader = openFile("file.txt") * withIn(someFile) { * // Reads a line from file.txt instead of default input * println(readLine) * } * }}} * * @param thunk the code to execute with * the new input stream active * * @return the results of `thunk` * @see `withIn[T](in:InputStream)(thunk: =>T)` */ def withIn[T](reader: Reader)(thunk: =>T): T = inVar.withValue(new BufferedReader(reader))(thunk) /** Sets the default input stream. * * @param in the new input stream. */ def setIn(in: InputStream) { setIn(new InputStreamReader(in)) } /** Sets 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 * @return the results of `thunk` * @see `withIn[T](reader:Reader)(thunk: =>T)` */ def withIn[T](in: InputStream)(thunk: =>T): T = withIn(new InputStreamReader(in))(thunk) /** Prints an object to `out` using its `toString` method. * * @param obj the object to print; may be null. */ def print(obj: Any) { out.print(if (null == obj) "null" else obj.toString()) } /** Flushes the output stream. This function is required when partial * output (i.e. output not terminated by a newline character) has * to be made visible on the terminal. */ def flush() { out.flush() } /** Prints a newline character on the default output. */ def println() { out.println() } /** Prints out an object to the default output, followed by a newline character. * * @param x the object to print. */ def println(x: Any) { out.println(x) } /** Prints its arguments as a formatted string to the default output, * based on a string pattern (in a fashion similar to printf in C). * * The interpretation of the formatting patterns is described in * <a href="" target="contentFrame" class="java/util/Formatter"> * `java.util.Formatter`</a>. * * @param text the pattern for formatting the arguments. * @param args the arguments used to instantiating the pattern. * @throws java.lang.IllegalArgumentException if there was a problem with the format string or arguments */ def printf(text: String, args: Any*) { out.print(text format (args : _*)) } /** Read a full line from the default input. Returns `null` if the end of the * input stream has been reached. * * @return the string read from the terminal or null if the end of stream was reached. */ def readLine(): String = in.readLine() /** Print formatted text to the default output and read a full line from the default input. * Returns `null` if the end of the input stream has been reached. * * @param text the format of the text to print out, as in `printf`. * @param args the parameters used to instantiate the format, as in `printf`. * @return the string read from the default input */ def readLine(text: String, args: Any*): String = { printf(text, args: _*) readLine() } /** Reads a boolean value from an entire line of the default input. * Has a fairly liberal interpretation of the input. * * @return the boolean value read, or false if it couldn't be converted to a boolean * @throws java.io.EOFException if the end of the input stream has been reached. */ def readBoolean(): Boolean = { val s = readLine() if (s == null) throw new java.io.EOFException("Console has reached end of input") else s.toLowerCase() match { case "true" => true case "t" => true case "yes" => true case "y" => true case _ => false } } /** Reads a byte value from an entire line of the default input. * * @return the Byte that was read * @throws java.io.EOFException if the end of the * input stream has been reached. * @throws java.lang.NumberFormatException if the value couldn't be converted to a Byte */ def readByte(): Byte = { val s = readLine() if (s == null) throw new java.io.EOFException("Console has reached end of input") else s.toByte } /** Reads a short value from an entire line of the default input. * * @return the short that was read * @throws java.io.EOFException if the end of the * input stream has been reached. * @throws java.lang.NumberFormatException if the value couldn't be converted to a Short */ def readShort(): Short = { val s = readLine() if (s == null) throw new java.io.EOFException("Console has reached end of input") else s.toShort } /** Reads a char value from an entire line of the default input. * * @return the Char that was read * @throws java.io.EOFException if the end of the * input stream has been reached. * @throws java.lang.StringIndexOutOfBoundsException if the line read from default input was empty */ def readChar(): Char = { val s = readLine() if (s == null) throw new java.io.EOFException("Console has reached end of input") else s charAt 0 } /** Reads an int value from an entire line of the default input. * * @return the Int that was read * @throws java.io.EOFException if the end of the * input stream has been reached. * @throws java.lang.NumberFormatException if the value couldn't be converted to an Int */ def readInt(): Int = { val s = readLine() if (s == null) throw new java.io.EOFException("Console has reached end of input") else s.toInt } /** Reads an long value from an entire line of the default input. * * @return the Long that was read * @throws java.io.EOFException if the end of the * input stream has been reached. * @throws java.lang.NumberFormatException if the value couldn't be converted to a Long */ def readLong(): Long = { val s = readLine() if (s == null) throw new java.io.EOFException("Console has reached end of input") else s.toLong } /** Reads a float value from an entire line of the default input. * @return the Float that was read. * @throws java.io.EOFException if the end of the * input stream has been reached. * @throws java.lang.NumberFormatException if the value couldn't be converted to a Float * */ def readFloat(): Float = { val s = readLine() if (s == null) throw new java.io.EOFException("Console has reached end of input") else s.toFloat } /** Reads a double value from an entire line of the default input. * * @return the Double that was read. * @throws java.io.EOFException if the end of the * input stream has been reached. * @throws java.lang.NumberFormatException if the value couldn't be converted to a Float */ def readDouble(): Double = { val s = readLine() if (s == null) throw new java.io.EOFException("Console has reached end of input") else s.toDouble } /** Reads in some structured input (from the default input), specified by * a format specifier. See class `java.text.MessageFormat` for details of * the format specification. * * @param format the format of the input. * @return a list of all extracted values. * @throws java.io.EOFException if the end of the input stream has been * reached. */ def readf(format: String): List[Any] = { val s = readLine() if (s == null) throw new java.io.EOFException("Console has reached end of input") else textComponents(new MessageFormat(format).parse(s)) } /** Reads in some structured input (from the default input), specified by * a format specifier, returning only the first value extracted, according * to the format specification. * * @param format format string, as accepted by `readf`. * @return The first value that was extracted from the input */ def readf1(format: String): Any = readf(format).head /** Reads in some structured input (from the default input), specified * by a format specifier, returning only the first two values extracted, * according to the format specification. * * @param format format string, as accepted by `readf`. * @return A [[scala.Tuple2]] containing the first two values extracted */ def readf2(format: String): (Any, Any) = { val res = readf(format) (res.head, res.tail.head) } /** Reads in some structured input (from the default input), specified * by a format specifier, returning only the first three values extracted, * according to the format specification. * * @param format format string, as accepted by `readf`. * @return A [[scala.Tuple3]] containing the first three values extracted */ def readf3(format: String): (Any, Any, Any) = { val res = readf(format) (res.head, res.tail.head, res.tail.tail.head) } private def textComponents(a: Array[AnyRef]): List[Any] = { var i: Int = a.length - 1 var res: List[Any] = Nil while (i >= 0) { res = (a(i) match { case x: java.lang.Boolean => x.booleanValue() case x: java.lang.Byte => x.byteValue() case x: java.lang.Short => x.shortValue() case x: java.lang.Character => x.charValue() case x: java.lang.Integer => x.intValue() case x: java.lang.Long => x.longValue() case x: java.lang.Float => x.floatValue() case x: java.lang.Double => x.doubleValue() case x => x }) :: res; i -= 1 } res } }