minor refactor to tidy up the API
This commit is contained in:
@@ -1,14 +1,21 @@
|
|||||||
package kotlin.template
|
package kotlin.template
|
||||||
|
|
||||||
import kotlin.dom.*
|
import kotlin.dom. *
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
import com.sun.org.apache.xalan.internal.xsltc.dom.UnionIterator
|
import com.sun.org.apache.xalan.internal.xsltc.dom.UnionIterator
|
||||||
|
import java.util.Locale
|
||||||
|
import java.text.NumberFormat
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
// TODO this class should move into the runtime
|
// TODO this class should move into the runtime
|
||||||
// in jet.StringTemplate
|
// in jet.StringTemplate
|
||||||
class StringTemplate(val values: Array<Any?>) {
|
class StringTemplate(val values : Array<Any?>) {
|
||||||
|
|
||||||
fun toString(): String {
|
/**
|
||||||
|
* Converts the template into a String
|
||||||
|
*/
|
||||||
|
fun toString() : String {
|
||||||
val out = StringBuilder()
|
val out = StringBuilder()
|
||||||
forEach{ out.append(it) }
|
forEach{ out.append(it) }
|
||||||
return out.toString() ?: ""
|
return out.toString() ?: ""
|
||||||
@@ -17,20 +24,31 @@ class StringTemplate(val values: Array<Any?>) {
|
|||||||
/**
|
/**
|
||||||
* Performs the given function on each value in the collection
|
* Performs the given function on each value in the collection
|
||||||
*/
|
*/
|
||||||
fun forEach(fn: (Any?) -> Unit): Unit {
|
fun forEach(fn : (Any?) -> Unit) : Unit {
|
||||||
for (v in values) {
|
for (v in values) {
|
||||||
fn(v)
|
fn(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun StringTemplate.toString(formatter : ValueFormatter): String {
|
/**
|
||||||
|
* Converts the string template into a string using the given formatter
|
||||||
|
* to encode values as Strings performing any special encoding (such as for HTML)
|
||||||
|
* or internationalisation.
|
||||||
|
*
|
||||||
|
* See [[HtmlFormatter] and [[LocaleFormatter] respectively.
|
||||||
|
*/
|
||||||
|
fun StringTemplate.toString(formatter : Formatter) : String {
|
||||||
val buffer = StringBuilder()
|
val buffer = StringBuilder()
|
||||||
append(buffer, formatter)
|
append(buffer, formatter)
|
||||||
return buffer.toString() ?: ""
|
return buffer.toString() ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
fun StringTemplate.append(out: Appendable, formatter: ValueFormatter): Unit {
|
/**
|
||||||
|
* Appends the text representation of this string template to the given output
|
||||||
|
* using the supplied formatter
|
||||||
|
*/
|
||||||
|
fun StringTemplate.append(out : Appendable, formatter : Formatter) : Unit {
|
||||||
var constantText = true
|
var constantText = true
|
||||||
this.forEach {
|
this.forEach {
|
||||||
if (constantText) {
|
if (constantText) {
|
||||||
@@ -49,43 +67,106 @@ fun StringTemplate.append(out: Appendable, formatter: ValueFormatter): Unit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
fun StringTemplate.toHtml(formatter : HtmlFormatter = HtmlFormatter()): String = toString(formatter)
|
* Converts this string template to internationalised text using the supplied
|
||||||
|
* [[LocaleFormatter]]
|
||||||
|
*/
|
||||||
|
fun StringTemplate.toLocale(formatter : LocaleFormatter = LocaleFormatter()) : String = toString(formatter)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a formatter of values in a [[StringTemplate]] which understands how to escape
|
* Converts this string template to HTML text
|
||||||
* different types for different kinds of language
|
|
||||||
*/
|
*/
|
||||||
trait ValueFormatter {
|
fun StringTemplate.toHtml(formatter : HtmlFormatter = HtmlFormatter()) : String = toString(formatter)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a formatter and encoder of values in a [[StringTemplate]] which understands
|
||||||
|
* how to format values for a particular [[Locale]] such as with the [[LocaleFormatter]] or
|
||||||
|
* to escape particular characters in different output formats such as [[HtmlFormatter]
|
||||||
|
*/
|
||||||
|
trait Formatter {
|
||||||
fun format(buffer : Appendable, val value : Any?) : Unit
|
fun format(buffer : Appendable, val value : Any?) : Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
object ToStringFormatter : ValueFormatter {
|
/**
|
||||||
|
* Formats strings with no special encoding other than allowing the null text to be
|
||||||
|
* configured
|
||||||
|
*/
|
||||||
|
open class ToStringFormatter : Formatter {
|
||||||
|
|
||||||
fun toString() = "ToStringFormatter"
|
var nullString : String = "null"
|
||||||
|
|
||||||
override fun format(buffer : Appendable, value : Any?) {
|
open fun toString() = "ToStringFormatter"
|
||||||
val text = if (value == null) "null" else value.toString()
|
|
||||||
buffer.append(text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HtmlFormatter : ValueFormatter {
|
override fun format(out : Appendable, value : Any?) {
|
||||||
var nullText : String = ""
|
|
||||||
|
|
||||||
override fun format(buffer : Appendable, value : Any?) {
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
buffer.append(nullText)
|
format(out, nullString)
|
||||||
} else if (value is StringTemplate) {
|
} else if (value is StringTemplate) {
|
||||||
value.append(buffer, this)
|
value.append(out, this)
|
||||||
} else if (value is Node) {
|
|
||||||
buffer.append(value.toXmlString())
|
|
||||||
} else {
|
} else {
|
||||||
escape(buffer, value.toString() ?: "")
|
format(out, value.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun escape(buffer : Appendable, text : String) : Unit {
|
/**
|
||||||
|
* Formats the given string allowing derived classes to override this method
|
||||||
|
* to escape strings with special characters such as for HTML
|
||||||
|
*/
|
||||||
|
open fun format(out : Appendable, text : String) : Unit {
|
||||||
|
out.append(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected val defaultLocale : Locale = Locale.getDefault().sure()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats values using a given [[Locale]] for internationalisation
|
||||||
|
*/
|
||||||
|
open class LocaleFormatter(val locale : Locale = defaultLocale) : ToStringFormatter() {
|
||||||
|
|
||||||
|
override fun toString() = "LocaleFormatter{$locale}"
|
||||||
|
|
||||||
|
public var numberFormat : NumberFormat = NumberFormat.getInstance(locale).sure()
|
||||||
|
|
||||||
|
public var dateFormat : DateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, locale).sure()
|
||||||
|
|
||||||
|
override fun format(out : Appendable, value : Any?) {
|
||||||
|
/* TODO KT-1583 - uncomment when its fixed
|
||||||
|
if (value is Number) {
|
||||||
|
format(out, format(value))
|
||||||
|
} else
|
||||||
|
*/
|
||||||
|
if (value is Date) {
|
||||||
|
format(out, format(value))
|
||||||
|
} else {
|
||||||
|
super.format(out, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun format(number : Number) : String {
|
||||||
|
return numberFormat.format(number) ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun format(date : Date) : String {
|
||||||
|
return dateFormat.format(date) ?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats values for HTML encoding, escaping special characters in HTML.
|
||||||
|
*/
|
||||||
|
class HtmlFormatter(locale : Locale = defaultLocale) : LocaleFormatter(locale) {
|
||||||
|
|
||||||
|
override fun toString() = "HtmlFormatter{$locale}"
|
||||||
|
|
||||||
|
override fun format(out : Appendable, value : Any?) {
|
||||||
|
if (value is Node) {
|
||||||
|
out.append(value.toXmlString())
|
||||||
|
} else {
|
||||||
|
super.format(out, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun format(buffer : Appendable, text : String) : Unit {
|
||||||
for (c in text) {
|
for (c in text) {
|
||||||
if (c == '<') buffer.append("<")
|
if (c == '<') buffer.append("<")
|
||||||
else if (c == '>') buffer.append(">")
|
else if (c == '>') buffer.append(">")
|
||||||
@@ -95,3 +176,5 @@ class HtmlFormatter : ValueFormatter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package template
|
||||||
|
|
||||||
|
import kotlin.template.*
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class LocaleTemplateTest : TestCase() {
|
||||||
|
fun testDefaultLocale() : Unit {
|
||||||
|
format(LocaleFormatter())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testFrance() : Unit {
|
||||||
|
format(LocaleFormatter(Locale.FRANCE.sure()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testGermany() : Unit {
|
||||||
|
format(LocaleFormatter(Locale.GERMANY.sure()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun format(formatter: LocaleFormatter): 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.toLocale("hello $name!")
|
||||||
|
|
||||||
|
// TODO will use a tuple soon
|
||||||
|
//val actual = formatter.format(StringTemplate(Tuple2<String,String>("hello ", name))
|
||||||
|
val actual = StringTemplate(array("hello ", name,
|
||||||
|
// TODO currently numbers cause: java.lang.ClassNotFoundException: jet.Number
|
||||||
|
// " price ", price,
|
||||||
|
" data ", now)).toString(formatter)
|
||||||
|
|
||||||
|
println("Got text: $actual")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user