From 5805165793d9abf3b0b9282927f49feb1b8d431b Mon Sep 17 00:00:00 2001 From: James Strachan Date: Mon, 12 Mar 2012 11:51:35 +0000 Subject: [PATCH] initial spike to show how KT-1565 might look for auto-escaping of HTML/XML/JSON/JDBC/URLs etc --- .../src/kotlin/template/HtmlTemplate.kt | 64 +++++++++++++++++++ .../src/kotlin/template/StringTemplate.kt | 44 +++++++++++++ .../testlib/test/template/HtmlTemplateTest.kt | 22 +++++++ .../test/template/StringTemplateTest.kt | 20 ++++++ 4 files changed, 150 insertions(+) create mode 100644 libraries/stdlib/src/kotlin/template/HtmlTemplate.kt create mode 100644 libraries/stdlib/src/kotlin/template/StringTemplate.kt create mode 100644 libraries/testlib/test/template/HtmlTemplateTest.kt create mode 100644 libraries/testlib/test/template/StringTemplateTest.kt diff --git a/libraries/stdlib/src/kotlin/template/HtmlTemplate.kt b/libraries/stdlib/src/kotlin/template/HtmlTemplate.kt new file mode 100644 index 00000000000..b6853ce892f --- /dev/null +++ b/libraries/stdlib/src/kotlin/template/HtmlTemplate.kt @@ -0,0 +1,64 @@ +package kotlin.template + +import kotlin.dom.toXmlString +import org.w3c.dom.Node + + +/** + * Creates a string template from a string with $ expressions inside using HTML escaping of expressions. + */ +// TODO varargs on constructors seems to fail +//class HtmlTemplate(vararg val text: String) { +class HtmlTemplate(val constantText : Array) { + + /** + * Creates a builder of string expressions which use HTML encoding on expressions + */ + fun builder() : HtmlTemplateBuilder { + // TODO we should allow the caller to pass these in when we create the builder! + val options = HtmlTemplateOptions() + return HtmlTemplateBuilder(constantText, options) + } +} + +class HtmlTemplateOptions() { + var nullText : String = "" +} + +open class HtmlTemplateBuilder(constantText : Array, val options : HtmlTemplateOptions) : StringTemplateBuilder(constantText) { + + override fun expression(expression : Any) : Unit { + val text = if (expression != null) expression.toString() ?: "" else options.nullText + escape(text) + } + + /** + * Appends the DOM node, no HTML escaping is done as we assume its already escaped + */ + fun expression(node : Node) : Unit { + // no need to escape + unescape(node.toXmlString(false)) + } + + /** + * Appends the given text escaped properly + */ + fun escape(text : String): Unit { + appendNextConstant() + for (c in text) { + if (c == '<') buffer.append("<") + else if (c == '>') buffer.append(">") + else if (c == '&') buffer.append("&") + else if (c == '"') buffer.append(""") + else buffer.append(c) + } + } + + /** + * Appends the unescaped text + */ + fun unescape(text : String) { + appendNextConstant() + buffer.append(text) + } +} diff --git a/libraries/stdlib/src/kotlin/template/StringTemplate.kt b/libraries/stdlib/src/kotlin/template/StringTemplate.kt new file mode 100644 index 00000000000..e9cdd89b411 --- /dev/null +++ b/libraries/stdlib/src/kotlin/template/StringTemplate.kt @@ -0,0 +1,44 @@ +package kotlin.template + +/** + * Creates a string template from a string with $ expressions inside. + */ +// TODO varargs on constructors seems to fail +//class StringTemplate(vararg val text: String) { +open class StringTemplate(val constantText : Array) { + + /** + * Creates a builder of string expressions + */ + open fun builder() : StringTemplateBuilder = StringTemplateBuilder(constantText) +} + +/** + * Used to build strings using expressions + */ +open class StringTemplateBuilder(val constantText : Array){ + protected val buffer: StringBuilder = StringBuilder() + var index : Int = 0 + + fun build() : String { + while (appendNextConstant()) {} + return buffer.toString() ?: "" + } + + open fun expression(expression : Any) { + appendNextConstant() + buffer.append(expression) + } + + /** + * Returns the next static text value from the template + */ + protected fun appendNextConstant(): Boolean { + if (index < constantText.size) { + buffer.append(constantText[index++]) + return true + } else { + return false + } + } +} \ No newline at end of file diff --git a/libraries/testlib/test/template/HtmlTemplateTest.kt b/libraries/testlib/test/template/HtmlTemplateTest.kt new file mode 100644 index 00000000000..f31b8d4cd37 --- /dev/null +++ b/libraries/testlib/test/template/HtmlTemplateTest.kt @@ -0,0 +1,22 @@ +package test.template + +import junit.framework.TestCase +import kotlin.template.* +import kotlin.test.assertEquals + +class HtmlTemplateTest : TestCase() { + fun testTemplate(): Unit { + val foo = "James" + val bar = "x > 1" + + // Code generated by the following template expression: + // + // val actual = [Html]"

$foo

hey $bar

" + val builder = HtmlTemplate(array("

", "

hey ", "

")).builder() + builder.expression(foo) + builder.expression(bar) + val actual = builder.build() + + assertEquals("

James

hey x > 1

", actual) + } +} \ No newline at end of file diff --git a/libraries/testlib/test/template/StringTemplateTest.kt b/libraries/testlib/test/template/StringTemplateTest.kt new file mode 100644 index 00000000000..2bd12e7291e --- /dev/null +++ b/libraries/testlib/test/template/StringTemplateTest.kt @@ -0,0 +1,20 @@ +package test.template + +import junit.framework.TestCase +import kotlin.template.StringTemplate +import kotlin.test.assertEquals + +class StringTemplateTest : TestCase() { + fun testTemplate(): Unit { + val name = "James" + + // Code generated by the following template expression: + // + // val actual = "hello $name!" + val builder = StringTemplate(array("hello ", "!")).builder() + builder.expression(name) + val actual = builder.build() + + assertEquals("hello James!", actual) + } +} \ No newline at end of file