/** * This is an example of a Type-Safe Groovy-style Builder * * Builders are good for declaratively describing data in your code. * In this example we show how to describe an HTML page in Kotlin. * * See this page for details: * https://confluence.jetbrains.net/display/Kotlin/Type-safe+Groovy-style+builders */ fun main(args: Array) { val result = 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 = "https://jetbrains.com/kotlin") { +"Kotlin" } // mixed content p { +"This is some" b { +"mixed" } +"text. For more see the" a(href = "https://jetbrains.com/kotlin") { +"Kotlin" } +"project" } p { +"some text" } // content generated from command-line arguments p { +"Command line arguments were:" ul { for (arg in args) li { +arg } } } } } println(result) } abstract class Element { abstract fun render(builder: StringBuilder, indent: String) override fun toString(): String { val builder = StringBuilder() render(builder, "") return builder.toString() } } class TextElement(val text: String) : Element() { override fun render(builder: StringBuilder, indent: String) { builder.append("$indent$text\n") } } abstract class Tag(val name: String) : Element() { val children = ArrayList() val attributes = HashMap() protected fun initTag(tag: T, init: T.() -> Unit): T { tag.init() children.add(tag) return tag } override fun render(builder: StringBuilder, indent: String) { builder.append("$indent<$name${renderAttributes()}>\n") for (c in children) { c.render(builder, indent + " ") } builder.append("$indent\n") } private fun renderAttributes(): String? { val builder = StringBuilder() for (a in attributes.keys) { builder.append(" $a=\"${attributes[a]}\"") } return builder.toString() } } abstract class TagWithText(name: String) : Tag(name) { operator fun String.unaryPlus() { children.add(TextElement(this)) } } class HTML() : TagWithText("html") { fun head(init: Head.() -> Unit) = initTag(Head(), init) fun body(init: Body.() -> Unit) = initTag(Body(), init) } class Head() : TagWithText("head") { fun title(init: Title.() -> Unit) = initTag(Title(), init) } class Title() : TagWithText("title") abstract class BodyTag(name: String) : TagWithText(name) { fun b(init: B.() -> Unit) = initTag(B(), init) fun p(init: P.() -> Unit) = initTag(P(), init) fun h1(init: H1.() -> Unit) = initTag(H1(), init) fun ul(init: UL.() -> Unit) = initTag(UL(), init) fun a(href: String, init: A.() -> Unit) { val a = initTag(A(), init) a.href = href } } class Body() : BodyTag("body") class UL() : BodyTag("ul") { fun li(init: LI.() -> Unit) = initTag(LI(), init) } class B() : BodyTag("b") class LI() : BodyTag("li") class P() : BodyTag("p") class H1() : BodyTag("h1") class A() : BodyTag("a") { public var href: String get() = attributes["href"]!! set(value) { attributes["href"] = value } } fun html(init: HTML.() -> Unit): HTML { val html = HTML() html.init() return html }