From 8d47332cd9953d500c522ee7802ae9032e1d67dc Mon Sep 17 00:00:00 2001 From: James Strachan Date: Tue, 13 Mar 2012 12:18:25 +0000 Subject: [PATCH] refactored the template experiment1 implementation to add i18n support of numbers & dates --- .../kotlin/template/experiment1/Templates.kt | 129 +++++++++++++----- .../template/experiment1/HtmlTemplateTest.kt | 5 +- .../template/experiment1/I18nTemplateTest.kt | 46 +++++++ 3 files changed, 143 insertions(+), 37 deletions(-) create mode 100644 libraries/testlib/test/template/experiment1/I18nTemplateTest.kt diff --git a/libraries/stdlib/src/kotlin/template/experiment1/Templates.kt b/libraries/stdlib/src/kotlin/template/experiment1/Templates.kt index 24ab5a0028d..e28ac196edc 100644 --- a/libraries/stdlib/src/kotlin/template/experiment1/Templates.kt +++ b/libraries/stdlib/src/kotlin/template/experiment1/Templates.kt @@ -3,6 +3,12 @@ package kotlin.template.experiment1 import org.w3c.dom.Node import kotlin.dom.toXmlString +import java.lang.Number +import java.text.DateFormat +import java.text.NumberFormat +import java.util.Date +import java.util.Locale + fun StringBuilder.text(value : String) : Unit { this.append(value) } @@ -22,43 +28,61 @@ fun format(fn : (StringBuilder) -> Unit) : String { } -abstract class TextBuilder(val output: Appendable) { +abstract class TextBuilder(val output : Appendable) { -/* + /* */ -/** - * Converts the given string template to a String + /** + * Converts the given string template to a String - fun toString(fn: (T) -> Unit): String = toString(ToStringFormatter, fn) + fun toString(fn: (T) -> Unit): String = toString(ToStringFormatter, fn) - fun toString(formatter : ValueFormatter, fn: (T) -> Unit): String { - fn(this) - return output.toString() ?: "" - } -*/ + fun toString(formatter : ValueFormatter, fn: (T) -> Unit): String { + fn(this) + return output.toString() ?: "" + } + */ - fun text(value: String): Unit { + fun text(value : String) : Unit { output.append(value) } - } -class HtmlBuilder(output: Appendable) : TextBuilder(output) { - var nullText : String = "" +open class I18nBuilder(output : Appendable, val formatter : I18nFormatter) : TextBuilder(output) { - fun expression(value: Node): Unit { + fun expression(value : Any?) : Unit { + escape(formatter.format(value)) + } + + fun expression(value : Number) : Unit { + escape(formatter.formatNumber(value)) + } + + fun expression(value : Date) : Unit { + escape(formatter.formatDate(value)) + } + + /** + * By default does no escaping but inherited classes may escape text + */ + open fun escape(text : String) : Unit { + output.append(text) + } +} + +open class HtmlBuilder(output : Appendable, formatter : I18nFormatter) : I18nBuilder(output, formatter) { + + /** + * Nodes are assumed to already be properly HTML/XML encoded so don't escape + */ + fun expression(value : Node) : Unit { text(value.toXmlString()) } - fun expression(value: Any?): Unit { - if (value == null) { - output.append(nullText) - } else { - escape(value.toString() ?: "") - } - } - - fun escape(text : String) : Unit { + /** + * Escapes text using HTML escaping of sensitive tokens + */ + override fun escape(text : String) : Unit { for (c in text) { if (c == '<') output.append("<") else if (c == '>') output.append(">") @@ -75,22 +99,57 @@ class HtmlBuilder(output: Appendable) : TextBuilder(output) { * different types for different kinds of language */ trait ValueFormatter { - fun format(buffer : Appendable, val value : Any?) : Unit + fun format(val value : Any?) : String } -object ToStringFormatter : ValueFormatter { +open class ToStringFormatter : ValueFormatter { - fun toString() = "ToStringFormatter" + var nullText : String = "null" - override fun format(buffer : Appendable, value : Any?) { - val text = if (value == null) "null" else value.toString() - buffer.append(text) + open fun toString() = "ToStringFormatter" + + override fun format(value : Any?): String { + return if (value == null) nullText else value.toString() } } -fun toHtml(fn: (HtmlBuilder) -> Unit): String { - val buffer = StringBuilder() - val html = HtmlBuilder(buffer) - fn(html) - return buffer.toString() ?: "" +open class I18nFormatter(val locale : Locale = Locale.getDefault().sure()): ToStringFormatter() { + + override fun toString() = "I18nFormatter" + + public var numberFormat : NumberFormat = NumberFormat.getInstance(locale).sure() + + public var dateFormat : DateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, locale).sure() + + fun formatNumber(number : Number): String { + return numberFormat.format(number) ?: "" + } + + fun formatDate(date : Date): String { + return dateFormat.format(date) ?: "" + } + + /** + * Generates the internationalised text for this formatter + */ + fun toString(fn : (I18nBuilder) -> Unit) : String { + val buffer = StringBuilder() + val builder = I18nBuilder(buffer, this) + fn(builder) + return buffer.toString() ?: "" + } + + /** + * Generates the HTML for the given function + */ + fun toHtml(fn : (HtmlBuilder) -> Unit) : String { + val buffer = StringBuilder() + val html = HtmlBuilder(buffer, this) + fn(html) + return buffer.toString() ?: "" + } } + + + + diff --git a/libraries/testlib/test/template/experiment1/HtmlTemplateTest.kt b/libraries/testlib/test/template/experiment1/HtmlTemplateTest.kt index 7ee0377f8ef..6d99e311295 100644 --- a/libraries/testlib/test/template/experiment1/HtmlTemplateTest.kt +++ b/libraries/testlib/test/template/experiment1/HtmlTemplateTest.kt @@ -13,10 +13,11 @@ class HtmlTemplateTest : TestCase() { val foo = "James" val bar = "x > 1" + val format = I18nFormatter() // Code generated by the following template expression: // - // val actual = toHtml("

$foo

hey $bar

") - val actual = toHtml{ + // val actual = format.toHtml("

$foo

hey $bar

") + val actual = format.toHtml{ it.text("

") it.expression(foo) it.text("

hey ") diff --git a/libraries/testlib/test/template/experiment1/I18nTemplateTest.kt b/libraries/testlib/test/template/experiment1/I18nTemplateTest.kt new file mode 100644 index 00000000000..9e7ba9a0d95 --- /dev/null +++ b/libraries/testlib/test/template/experiment1/I18nTemplateTest.kt @@ -0,0 +1,46 @@ +package template.experiment1 + +import kotlin.template.experiment1.* +import kotlin.test.assertEquals + +import junit.framework.TestCase +import java.util.Date +import java.util.Locale + +class I18nTemplateTest : TestCase() { + fun testDefaultLocale() : Unit { + format(I18nFormatter()) + } + + fun testFrance() : Unit { + format(I18nFormatter(Locale.FRANCE.sure())) + } + + fun testGermany() : Unit { + format(I18nFormatter(Locale.GERMANY.sure())) + } + + fun format(format: I18nFormatter): Unit { + val name = "James" + // TODO currently numbers cause: java.lang.ClassNotFoundException: jet.Number + //val price: Double = 1.99 + val now = Date() + + + // Code generated by the following template expression: + // + // val actual = format.toI18n("hello $name!") + val actual = format.toString{ + it.text("hello ") + it.expression(name) +/* + it.text(" price ") + it.expression(price) +*/ + it.text(" date ") + it.expression(now) + } + + println("Got text: $actual") + } +}