moved old experimental code into the sandbox
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
package kotlin.template
|
||||
|
||||
import kotlin.io.*
|
||||
|
||||
/**
|
||||
* Represents a generic API to templates which should be usable
|
||||
* in JavaScript in a browser or on the server side stand alone or in a web app etc.
|
||||
*
|
||||
* To make things easier to implement in JS this namespace won't refer to any java.io or servlet
|
||||
* stuff
|
||||
*/
|
||||
trait Template {
|
||||
var printer: Printer
|
||||
|
||||
/** Renders the template to the output printer */
|
||||
fun render(): Unit
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the output of a template which is a stream of objects
|
||||
* usually strings which then write to some underlying output stream
|
||||
* such as a java.io.Writer on the server side.
|
||||
*
|
||||
* We abstract away java.io.* APIs here to make the JS implementation simpler
|
||||
*/
|
||||
trait Printer {
|
||||
fun print(value: Any): Unit
|
||||
//fun print(text: String): Unit
|
||||
}
|
||||
|
||||
class NullPrinter() : Printer {
|
||||
//override fun print(text: String) = none()
|
||||
override fun print(value: Any) {
|
||||
throw UnsupportedOperationException("No Printer defined on the Template")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for template implementations to hold any helpful behaviour
|
||||
*/
|
||||
abstract class TemplateSupport : Template {
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for templates generating text output
|
||||
* by printing values to some underlying Printer which
|
||||
* will typically output to a String, File, stream etc.
|
||||
*/
|
||||
abstract class TextTemplate() : TemplateSupport(), Printer {
|
||||
override var printer: Printer = NullPrinter()
|
||||
|
||||
fun String.plus(): Unit {
|
||||
printer.print(this)
|
||||
}
|
||||
|
||||
//override fun print(value: String) = printer.print(value)
|
||||
|
||||
override fun print(value: Any) = printer.print(value)
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package kotlin.template.html
|
||||
|
||||
import kotlin.*
|
||||
import kotlin.template.*
|
||||
import kotlin.io.*
|
||||
import kotlin.util.*
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
|
||||
|
||||
trait Factory<T> {
|
||||
fun create() : T
|
||||
}
|
||||
|
||||
abstract class Element {
|
||||
abstract fun appendTo(builder: StringBuilder): Unit
|
||||
|
||||
}
|
||||
|
||||
class TextElement(val text : String) : Element() {
|
||||
override fun appendTo(builder: StringBuilder) {
|
||||
builder.append(text)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Tag(val name : String) : Element() {
|
||||
val children = ArrayList<Element>()
|
||||
val attributes = HashMap<String, String>()
|
||||
|
||||
protected fun initTag<T : Element>(init : T.()-> Unit) : T
|
||||
where class object T : Factory<T> {
|
||||
val tag = try {
|
||||
T.create()
|
||||
} catch (e: NullPointerException) {
|
||||
val typeName = javaClass.getName()
|
||||
throw UnsupportedOperationException("No class object create() method for $typeName")
|
||||
}
|
||||
tag.init()
|
||||
children.add(tag)
|
||||
return tag
|
||||
}
|
||||
|
||||
override fun appendTo(builder: StringBuilder): Unit {
|
||||
builder.append("<")
|
||||
builder.append(name)
|
||||
if (!attributes.isEmpty()) {
|
||||
for (e in attributes.entrySet()) {
|
||||
if (e != null) {
|
||||
builder.append(" ${e.getKey()}=\"${e.getValue()}\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (children.isEmpty()) {
|
||||
builder.append("/>")
|
||||
} else {
|
||||
builder.append(">")
|
||||
for (c in children) {
|
||||
c.appendTo(builder)
|
||||
}
|
||||
builder.append("<")
|
||||
builder.append(name)
|
||||
builder.append(">")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun toString(): String {
|
||||
val builder = StringBuilder()
|
||||
appendTo(builder)
|
||||
return builder.toString().sure()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TagWithText(name : String) : Tag(name) {
|
||||
fun String.plus() {
|
||||
children.add(TextElement(this))
|
||||
}
|
||||
}
|
||||
|
||||
class HTML() : TagWithText("html") {
|
||||
class object : Factory<HTML> {
|
||||
override fun create() = HTML()
|
||||
}
|
||||
|
||||
fun head(init : Head.()-> Unit) = initTag(init)
|
||||
|
||||
fun body(init : Body.()-> Unit) = initTag(init)
|
||||
}
|
||||
|
||||
class Head() : TagWithText("head") {
|
||||
class object : Factory<Head> {
|
||||
override fun create() = Head()
|
||||
}
|
||||
|
||||
fun title(init : Title.()-> Unit) = initTag(init)
|
||||
}
|
||||
|
||||
class Title() : TagWithText("title") {
|
||||
class object : Factory<Title> {
|
||||
override fun create() = Title()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BodyTag(name : String) : TagWithText(name) {
|
||||
}
|
||||
|
||||
class Body() : BodyTag("body") {
|
||||
class object : Factory<Body> {
|
||||
override fun create() = Body()
|
||||
}
|
||||
|
||||
fun b(init : B.()-> Unit) = initTag(init)
|
||||
fun p(init : P.()-> Unit) = initTag(init)
|
||||
fun h1(init : H1.()-> Unit) = initTag(init)
|
||||
|
||||
fun a(href : String) {
|
||||
a(href, {})
|
||||
}
|
||||
|
||||
fun a(href : String, init : A.()-> Unit) {
|
||||
val a = initTag(init)
|
||||
a.href = href
|
||||
}
|
||||
}
|
||||
|
||||
class B() : BodyTag("b") {
|
||||
class object : Factory<B> {
|
||||
override fun create() = B()
|
||||
}
|
||||
}
|
||||
|
||||
class P() : BodyTag("p") {
|
||||
class object : Factory<P> {
|
||||
override fun create() = P()
|
||||
}
|
||||
}
|
||||
class H1() : BodyTag("h1") {
|
||||
class object : Factory<H1> {
|
||||
override fun create() = H1()
|
||||
}
|
||||
}
|
||||
|
||||
class A() : BodyTag("a") {
|
||||
class object : Factory<A> {
|
||||
override fun create() = A()
|
||||
}
|
||||
|
||||
var href : String
|
||||
get() = attributes["href"] ?: ""
|
||||
set(value) {
|
||||
attributes["href"] = value
|
||||
}
|
||||
}
|
||||
|
||||
fun body(init: Body.()-> Unit): Body {
|
||||
val elem = Body()
|
||||
elem.init()
|
||||
return elem
|
||||
}
|
||||
|
||||
fun html(init : HTML.()-> Unit) : HTML {
|
||||
val html = HTML()
|
||||
html.init()
|
||||
return html
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for templates which generate markup (XML or HTML for example)
|
||||
* which have additional helper methods for escaping markup etc
|
||||
*/
|
||||
abstract class MarkupTemplate() : TemplateSupport() {
|
||||
|
||||
override fun render() {
|
||||
val markup = markup()
|
||||
print(markup)
|
||||
}
|
||||
|
||||
/** Returns the markup to be rendered */
|
||||
abstract fun markup(): Iterable<Element>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Server side Java IO code to avoid coupling
|
||||
// the core template code to java.* for easier JS porting
|
||||
package kotlin.template.io
|
||||
|
||||
import kotlin.template.*
|
||||
import java.io.Writer
|
||||
import java.io.OutputStream
|
||||
import java.io.OutputStreamWriter
|
||||
import java.io.PrintStream
|
||||
import java.io.StringWriter
|
||||
|
||||
/**
|
||||
* A Printer implementation which uses a Writer
|
||||
*/
|
||||
class WriterPrinter(val writer: Writer) : Printer {
|
||||
|
||||
override fun print(value: Any) {
|
||||
// TODO should be using a formatter to do the conversion
|
||||
writer.write(value.toString())
|
||||
}
|
||||
}
|
||||
|
||||
fun Template.renderToText(): String {
|
||||
val buffer = StringWriter()
|
||||
renderTo(buffer)
|
||||
return buffer.toString().sure()
|
||||
}
|
||||
|
||||
fun Template.renderTo(writer: Writer): Unit {
|
||||
this.printer = WriterPrinter(writer)
|
||||
this.render()
|
||||
}
|
||||
|
||||
fun Template.renderTo(os: OutputStream): Unit = renderTo(OutputStreamWriter(os))
|
||||
@@ -0,0 +1,46 @@
|
||||
package kotlin.template
|
||||
|
||||
import kotlin.*
|
||||
import kotlin.template.io.*
|
||||
import kotlin.io.*
|
||||
import kotlin.util.*
|
||||
import kotlin.test.*
|
||||
import java.util.*
|
||||
|
||||
class EmailTemplate(var name: String = "James", var time: Date = Date()) : TextTemplate() {
|
||||
override fun render() {
|
||||
print("Hello there $name and how are you? Today is $time. Kotlin rocks")
|
||||
}
|
||||
}
|
||||
|
||||
class MoreDryTemplate(var name: String = "James", var time: Date = Date()) : TextTemplate() {
|
||||
override fun render() {
|
||||
+"Hey there $name and how are you? Today is $time. Kotlin rocks"
|
||||
}
|
||||
}
|
||||
|
||||
class TemplateCoreTest() : TestSupport() {
|
||||
fun testDefaultValues() {
|
||||
val text = EmailTemplate().renderToText()
|
||||
assert {
|
||||
println(text)
|
||||
text.startsWith("Hello there James")
|
||||
}
|
||||
}
|
||||
|
||||
fun testDifferentValues() {
|
||||
val text = EmailTemplate("Andrey").renderToText()
|
||||
assert {
|
||||
println(text)
|
||||
text.startsWith("Hello there Andrey")
|
||||
}
|
||||
}
|
||||
|
||||
fun testMoreDryTemplate() {
|
||||
val text = MoreDryTemplate("Alex").renderToText()
|
||||
assert {
|
||||
println(text)
|
||||
text.startsWith("Hey there Alex")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package kotlin.template.html
|
||||
|
||||
import kotlin.*
|
||||
import kotlin.template.*
|
||||
import kotlin.template.io.*
|
||||
import kotlin.io.*
|
||||
import kotlin.util.*
|
||||
import kotlin.test.*
|
||||
import java.util.*
|
||||
|
||||
val justBody = body {
|
||||
+"Hello world"
|
||||
}
|
||||
|
||||
fun result(args : List<String>) =
|
||||
html {
|
||||
head {
|
||||
title {+"XML encoding with Kotlin"}
|
||||
}
|
||||
body {
|
||||
h1 {+"XML encoding with Kotlin"}
|
||||
p {+"this format can be used as an alternative markup to XML"}
|
||||
|
||||
// an element with attributes and text content
|
||||
a(href = "http://jetbrains.com/kotlin") {+"Kotlin"}
|
||||
|
||||
// mixed content
|
||||
p {
|
||||
+"This is some"
|
||||
b {+"mixed"}
|
||||
+"text. For more see the"
|
||||
a(href = "http://jetbrains.com/kotlin") {+"Kotlin"}
|
||||
+"project"
|
||||
}
|
||||
p {+"some text"}
|
||||
|
||||
// content generated by
|
||||
p {
|
||||
for (arg in args)
|
||||
+arg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Create a bad element which doesn't have a class object create() method */
|
||||
class BadElem() : BodyTag("bad") {
|
||||
}
|
||||
|
||||
/** Creates a bad body to test out badly defined elements */
|
||||
class BadBody() : BodyTag("badBody") {
|
||||
fun bad(init : BadElem.()-> Unit) = initTag(init)
|
||||
}
|
||||
|
||||
fun badBody(init: BadBody.()-> Unit): BadBody {
|
||||
val elem = BadBody()
|
||||
elem.init()
|
||||
return elem
|
||||
}
|
||||
|
||||
class TemplateHtmlTest() : TestSupport() {
|
||||
|
||||
fun testJustBody() {
|
||||
assertEquals("<body>Hello world<body>", justBody.toString())
|
||||
}
|
||||
|
||||
fun testEmbeddedSimpleBody() {
|
||||
val e = body {
|
||||
+"body with text"
|
||||
}
|
||||
assertEquals("<body>body with text<body>", e.toString())
|
||||
}
|
||||
|
||||
fun testEmbeddedBodyWithNestedBold() {
|
||||
val e = body {
|
||||
b{
|
||||
+"this is bold"
|
||||
}
|
||||
}
|
||||
assertEquals("<body><b>this is bold<b><body>", e.toString())
|
||||
}
|
||||
|
||||
fun testEmbeddedBodyWithNestedLinks() {
|
||||
val e = body {
|
||||
a("http://jetbrains.com/kotlin") {
|
||||
+"link text"
|
||||
}
|
||||
}
|
||||
assertEquals("<body><a href=\"http://jetbrains.com/kotlin\">link text<a><body>", e.toString())
|
||||
}
|
||||
|
||||
fun testHtmlFunction() {
|
||||
val e = result(arrayList("a", "b", "c"))
|
||||
assertEquals("""<html><head><title>XML encoding with Kotlin<title><head><body><h1>XML encoding with Kotlin<h1><p>this format can be used as an alternative markup to XML<p><a href="http://jetbrains.com/kotlin">Kotlin<a><b>mixed<b><a href="http://jetbrains.com/kotlin">Kotlin<a><p>This is sometext. For more see theproject<p><p>some text<p><p>abc<p><body><html>""", e.toString())
|
||||
}
|
||||
|
||||
fun testEmbeddedFunction() {
|
||||
val e = html {
|
||||
head {
|
||||
title {+"XML encoding with Kotlin"}
|
||||
}
|
||||
body {
|
||||
a("http://jetbrains.com/kotlin")
|
||||
}
|
||||
}
|
||||
assertEquals("<html><head><title>XML encoding with Kotlin<title><head><body><a href=\"http://jetbrains.com/kotlin\"/><body><html>", e.toString())
|
||||
}
|
||||
|
||||
fun testBadlyDefinedElement() {
|
||||
failsWith<UnsupportedOperationException>{
|
||||
val e = badBody {
|
||||
bad{
|
||||
+"Bad Element Text"
|
||||
}
|
||||
}
|
||||
println("bad body: $e")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2010-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package kotlin.template;
|
||||
|
||||
import kotlin.template.html.*;
|
||||
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class TemplateTestAll {
|
||||
public static TestSuite suite() {
|
||||
return new TestSuite(TemplateCoreTest.class, TemplateHtmlTest.class);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user