diff --git a/javalib/source/src/java/util/Formattable.scala b/javalib/source/src/java/util/Formattable.scala new file mode 100644 index 0000000000..e651fbba24 --- /dev/null +++ b/javalib/source/src/java/util/Formattable.scala @@ -0,0 +1,5 @@ +package java.util + +trait Formattable { + def formatTo(formatter: Formatter, flags: Int, width: Int, precision: Int): Unit +} diff --git a/javalib/source/src/java/util/FormattableFlags.scala b/javalib/source/src/java/util/FormattableFlags.scala new file mode 100644 index 0000000000..de3e8e3ab9 --- /dev/null +++ b/javalib/source/src/java/util/FormattableFlags.scala @@ -0,0 +1,7 @@ +package java.util + +object FormattableFlags { + val ALTERNATE = 4 + val LEFT_JUSTIFY = 1 + val UPPERCASE = 2 +} diff --git a/javalib/source/src/java/util/Formatter.scala b/javalib/source/src/java/util/Formatter.scala index a9c68983cd..b0d420c5fb 100644 --- a/javalib/source/src/java/util/Formatter.scala +++ b/javalib/source/src/java/util/Formatter.scala @@ -72,8 +72,9 @@ final class Formatter(private val dest: Appendable) extends Closeable with Flush def hasFlag(flag: js.String) = flags.indexOf(flag) >= 0 val widthStr = matchResult(3) + val hasWidth = !(!widthStr) val width = - if (!(!widthStr)) js.parseInt(widthStr) + if (hasWidth) js.parseInt(widthStr) else (0: js.Number) val precisionStr = matchResult(4) @@ -175,10 +176,22 @@ final class Formatter(private val dest: Appendable) extends Closeable with Flush if (arg eq null) "null" else Integer.toHexString(arg.hashCode) } - case 's' | 'S' => pad { - val s: js.String = if (arg eq null) "null" else arg.toString() - if (hasPrecision) s.substring(0, precision) - else s + 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(numberArg)) diff --git a/test/src/test/scala/scala/scalajs/test/javalib/FormatterTest.scala b/test/src/test/scala/scala/scalajs/test/javalib/FormatterTest.scala index 1710cc2934..8e17b001f8 100644 --- a/test/src/test/scala/scala/scalajs/test/javalib/FormatterTest.scala +++ b/test/src/test/scala/scala/scalajs/test/javalib/FormatterTest.scala @@ -11,7 +11,7 @@ package javalib import scala.scalajs.js import scala.scalajs.test.ScalaJSTest -import java.util.{ Formatter, Formattable } +import java.util.{ Formatter, Formattable, FormattableFlags } import java.lang.{ Double => JDouble, @@ -27,6 +27,28 @@ import java.lang.{ object FormatterTest extends ScalaJSTest { class HelperClass + class FormattableClass extends Formattable { + var frm: Formatter = _ + var flags: Int = _ + var width: Int = _ + var precision: Int = _ + var calls = 0 + def formatTo(frm: Formatter, flags: Int, width: Int, precision: Int) = { + this.calls += 1 + this.flags = flags + this.width = width + this.precision = precision + frm.out().append("foobar") + } + + def expectCalled(times: Int, flags: Int, width: Int, precision: Int) = { + expect(this.calls).toEqual(times) + expect(this.flags).toEqual(flags) + expect(this.width).toEqual(width) + expect(this.precision).toEqual(precision) + } + + } def expectF(format: String, args: AnyRef*) = { val fmt = new Formatter() @@ -35,6 +57,13 @@ object FormatterTest extends ScalaJSTest { expect(res) } + def expectFC(format: String, flags: Int, width: Int, precision: Int) = { + val fc = new FormattableClass + val exp = expectF(format, fc) + fc.expectCalled(1, flags, width, precision) + exp + } + describe("java.util.Formatter") { it("should provide 'b' conversion") { @@ -51,6 +80,19 @@ object FormatterTest extends ScalaJSTest { expectF("%h", null).toEqual("null") } + it("should provide 's' conversion") { + expectFC("%s", 0, -1, -1).toEqual("foobar") + expectFC("%-s", FormattableFlags.LEFT_JUSTIFY, -1, -1).toEqual("foobar") + expectFC("%-10s", FormattableFlags.LEFT_JUSTIFY, 10, -1).toEqual("foobar") + expectFC("%#-10.2s", FormattableFlags.LEFT_JUSTIFY | + FormattableFlags.ALTERNATE, 10, 2).toEqual("foobar") + expectFC("%#10.2S", FormattableFlags.UPPERCASE | + FormattableFlags.ALTERNATE, 10, 2).toEqual("foobar") + expectF("%10s", "hello").toEqual(" hello") + expectF("%-10s", "hello").toEqual("hello ") + expectThrow("%#s", "hello") + } + it("should provide 'c' conversion") { expectF("%-5c", new Character('!')).toEqual("! ") }