initial spike to show how KT-1565 might look for auto-escaping of HTML/XML/JSON/JDBC/URLs etc
This commit is contained in:
@@ -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<String>) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<String>, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a builder of string expressions
|
||||||
|
*/
|
||||||
|
open fun builder() : StringTemplateBuilder = StringTemplateBuilder(constantText)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to build strings using expressions
|
||||||
|
*/
|
||||||
|
open class StringTemplateBuilder(val constantText : Array<String>){
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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]"<h1>$foo</h1> <p>hey $bar</p>"
|
||||||
|
val builder = HtmlTemplate(array("<h1>", "</h1> <p>hey ", "</p>")).builder()
|
||||||
|
builder.expression(foo)
|
||||||
|
builder.expression(bar)
|
||||||
|
val actual = builder.build()
|
||||||
|
|
||||||
|
assertEquals("<h1>James</h1> <p>hey x > 1</p>", actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user