summaryrefslogtreecommitdiff
path: root/examples/scala-js/javalib/src/main/scala/java/util/Formatter.scala
diff options
context:
space:
mode:
Diffstat (limited to 'examples/scala-js/javalib/src/main/scala/java/util/Formatter.scala')
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/Formatter.scala273
1 files changed, 273 insertions, 0 deletions
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/Formatter.scala b/examples/scala-js/javalib/src/main/scala/java/util/Formatter.scala
new file mode 100644
index 0000000..5e0ab22
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/Formatter.scala
@@ -0,0 +1,273 @@
+package java.util
+
+import scala.annotation.switch
+import scala.scalajs.js
+
+import java.io._
+import java.lang._
+
+final class Formatter(private val dest: Appendable) extends Closeable with Flushable {
+ import Formatter._
+
+ var closed = false
+
+ def this() = this(new StringBuilder())
+
+ def close(): Unit = {
+ if (!closed) {
+ dest match {
+ case cl: Closeable => cl.close()
+ case _ =>
+ }
+ }
+ closed = true
+ }
+
+ def flush(): Unit = ifNotClosed {
+ dest match {
+ case fl: Flushable => fl.flush()
+ case _ =>
+ }
+ }
+
+ // Begin implem of format()
+
+ def format(format_in: String, args: Array[AnyRef]): Formatter = ifNotClosed {
+ import js.Any.fromDouble // to have .toFixed and .toExponential on Doubles
+
+ var fmt: String = format_in
+ var lastImplicitIndex: Int = 0
+ var lastIndex: Int = 0 // required for < flag
+
+ while (!fmt.isEmpty) {
+ fmt match {
+ case RegularChunk(matchResult) =>
+ fmt = fmt.substring(matchResult(0).get.length)
+ dest.append(matchResult(0).get)
+
+ case DoublePercent(_) =>
+ fmt = fmt.substring(2)
+ dest.append('%')
+
+ case EOLChunk(_) =>
+ fmt = fmt.substring(2)
+ dest.append('\n')
+
+ case FormattedChunk(matchResult) =>
+ fmt = fmt.substring(matchResult(0).get.length)
+
+ val flags = matchResult(2).get
+ def hasFlag(flag: String) = flags.indexOf(flag) >= 0
+
+ val indexStr = matchResult(1).getOrElse("")
+ val index = if (!indexStr.isEmpty) {
+ Integer.parseInt(indexStr)
+ } else if (hasFlag("<")) {
+ lastIndex
+ } else {
+ lastImplicitIndex += 1
+ lastImplicitIndex
+ }
+ lastIndex = index
+ if (index <= 0 || index > args.length)
+ throw new MissingFormatArgumentException(matchResult(5).get)
+ val arg = args(index-1)
+
+ val widthStr = matchResult(3).getOrElse("")
+ val hasWidth = !widthStr.isEmpty
+ val width =
+ if (hasWidth) Integer.parseInt(widthStr)
+ else 0
+
+ val precisionStr = matchResult(4).getOrElse("")
+ val hasPrecision = !precisionStr.isEmpty
+ val precision =
+ if (hasPrecision) Integer.parseInt(precisionStr)
+ else 0
+
+ val conversion = matchResult(5).get.charAt(0)
+
+ def intArg: Int = (arg: Any) match {
+ case arg: Int => arg
+ case arg: Char => arg.toInt
+ }
+ def numberArg: scala.Double = (arg: Any) match {
+ case arg: Number => arg.doubleValue()
+ case arg: Char => arg.toDouble
+ }
+
+ def padCaptureSign(argStr: String, prefix: String) = {
+ val firstChar = argStr.charAt(0)
+ if (firstChar == '+' || firstChar == '-')
+ pad(argStr.substring(1), firstChar+prefix)
+ else
+ pad(argStr, prefix)
+ }
+
+ def strRepeat(s: String, times: Int) = {
+ var result: String = ""
+ var i = times
+ while (i > 0) {
+ result += s
+ i -= 1
+ }
+ result
+ }
+
+ def with_+(s: String, preventZero: scala.Boolean = false) = {
+ if (s.charAt(0) != '-') {
+ if (hasFlag("+"))
+ pad(s, "+", preventZero)
+ else if (hasFlag(" "))
+ pad(s, " ", preventZero)
+ else
+ pad(s, "", preventZero)
+ } else {
+ if (hasFlag("("))
+ pad(s.substring(1) + ")", "(", preventZero)
+ else
+ pad(s.substring(1), "-", preventZero)
+ }
+ }
+
+ def pad(argStr: String, prefix: String = "",
+ preventZero: Boolean = false) = {
+ val prePadLen = argStr.length + prefix.length
+
+ val padStr = {
+ if (width <= prePadLen) {
+ prefix + argStr
+ } else {
+ val padRight = hasFlag("-")
+ val padZero = hasFlag("0") && !preventZero
+ val padLength = width - prePadLen
+ val padChar: String = if (padZero) "0" else " "
+ val padding = strRepeat(padChar, padLength)
+
+ if (padZero && padRight)
+ throw new java.util.IllegalFormatFlagsException(flags)
+ else if (padRight) prefix + argStr + padding
+ else if (padZero) prefix + padding + argStr
+ else padding + prefix + argStr
+ }
+ }
+
+ val casedStr =
+ if (conversion.isUpper) padStr.toUpperCase()
+ else padStr
+ dest.append(casedStr)
+ }
+
+ (conversion: @switch) match {
+ case 'b' | 'B' => pad { arg match {
+ case null => "false"
+ case b: Boolean => String.valueOf(b)
+ case _ => "true"
+ } }
+ case 'h' | 'H' => pad {
+ if (arg eq null) "null"
+ else Integer.toHexString(arg.hashCode)
+ }
+ case 's' | 'S' => arg match {
+ case null if !hasFlag("#") => pad("null")
+ case formattable: Formattable =>
+ val flags = (
+ (if (hasFlag("-")) FormattableFlags.LEFT_JUSTIFY else 0) |
+ (if (hasFlag("#")) FormattableFlags.ALTERNATE else 0) |
+ (if (conversion.isUpper) FormattableFlags.UPPERCASE else 0)
+ )
+
+ formattable.formatTo(this, flags,
+ if (hasWidth) width.toInt else -1,
+ if (hasPrecision) precision.toInt else -1)
+ None // no further processing
+ case t: AnyRef if !hasFlag("#") => pad(t.toString)
+ case _ =>
+ throw new FormatFlagsConversionMismatchException("#", 's')
+ }
+ case 'c' | 'C' =>
+ pad(js.String.fromCharCode(intArg))
+ case 'd' =>
+ with_+(numberArg.toString())
+ case 'o' =>
+ val str = (arg: Any) match {
+ case arg: scala.Int => Integer.toOctalString(arg)
+ case arg: scala.Long => Long.toOctalString(arg)
+ }
+ padCaptureSign(str, if (hasFlag("#")) "0" else "")
+ case 'x' | 'X' =>
+ val str = (arg: Any) match {
+ case arg: scala.Int => Integer.toHexString(arg)
+ case arg: scala.Long => Long.toHexString(arg)
+ }
+ padCaptureSign(str, if (hasFlag("#")) "0x" else "")
+ case 'e' | 'E' =>
+ sciNotation(if (hasPrecision) precision else 6)
+ case 'g' | 'G' =>
+ val m = Math.abs(numberArg)
+ // precision handling according to JavaDoc
+ // precision here means number of significant digits
+ // not digits after decimal point
+ val p =
+ if (!hasPrecision) 6
+ else if (precision == 0) 1
+ else precision
+ // between 1e-4 and 10e(p): display as fixed
+ if (m >= 1e-4 && m < Math.pow(10, p)) {
+ val sig = Math.ceil(Math.log10(m))
+ with_+(numberArg.toFixed(Math.max(p - sig, 0)))
+ } else sciNotation(p - 1)
+ case 'f' =>
+ with_+({
+ // JavaDoc: 6 is default precision
+ numberArg.toFixed(if (hasPrecision) precision else 6)
+ }, numberArg.isNaN || numberArg.isInfinite)
+ }
+
+ def sciNotation(precision: Int) = {
+ val exp = numberArg.toExponential(precision)
+ with_+({
+ // check if we need additional 0 padding in exponent
+ // JavaDoc: at least 2 digits
+ if ("e" == exp.charAt(exp.length - 3)) {
+ exp.substring(0, exp.length - 1) + "0" +
+ exp.charAt(exp.length - 1)
+ } else exp
+ }, numberArg.isNaN || numberArg.isInfinite)
+ }
+ }
+ }
+
+ this
+ }
+
+ def ioException(): IOException = null
+ def locale(): Locale = ifNotClosed { null }
+ def out(): Appendable = ifNotClosed { dest }
+
+ override def toString(): String = out().toString()
+
+ @inline private def ifNotClosed[T](body: => T): T =
+ if (closed) throwClosedException()
+ else body
+
+ private def throwClosedException(): Nothing =
+ throw new FormatterClosedException()
+
+}
+
+object Formatter {
+
+ private class RegExpExtractor(val regexp: js.RegExp) {
+ def unapply(str: String): Option[js.RegExp.ExecResult] = {
+ Option(regexp.exec(str))
+ }
+ }
+
+ private val RegularChunk = new RegExpExtractor(new js.RegExp("""^[^\x25]+"""))
+ private val DoublePercent = new RegExpExtractor(new js.RegExp("""^\x25{2}"""))
+ private val EOLChunk = new RegExpExtractor(new js.RegExp("""^\x25n"""))
+ private val FormattedChunk = new RegExpExtractor(new js.RegExp(
+ """^\x25(?:([1-9]\d*)\$)?([-#+ 0,\(<]*)(\d*)(?:\.(\d+))?([A-Za-z])"""))
+
+}