当我们需要在APP上面发送一篇文章的时候,通常都需要文章能够支持编辑各种文本、字体颜色、甚至需要在文字的任意位置添加链接、图片,这时通常以一种特殊的形式保存文本,并且上传到服务器,下次在获取解析,以此还原文章。
本文将建基于 Kotlin 的适用于采用半声明方式构建复杂层次数据结构领域专用语(DSL)实现一个HTML构建器。代码如下:
interface Element {
fun render(builder: StringBuilder, indent: String)
}
class TextElement(private val text: String) : Element {
override fun render(builder: StringBuilder, indent: String) {
builder.append("$indent$text\n")
}
}
@DslMarker
annotation class HtmlTagMarker
@HtmlTagMarker
abstract class Tag(val name: String) : Element {
val children = arrayListOf<Element>()
val attributes = hashMapOf<String, String>()
protected fun <T : Element> 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</$name>\n")
}
private fun renderAttributes(): String {
val builder = StringBuilder()
for ((attr, value) in attributes) {
builder.append(" $attr="$value"")
}
return builder.toString()
}
override fun toString(): String {
val builder = StringBuilder()
render(builder, "")
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") {
fun h1(init: H1.() -> Unit) = initTag(H1(), init)
}
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 a(href: String, init: A.() -> Unit) {
val a = initTag(A(), init)
a.href = href
}
}
class Body : BodyTag("body")
class B : BodyTag("b")
class P : BodyTag("p")
class H1 : BodyTag("h1")
class A : BodyTag("a") {
var href: String
get() = attributes["href"]!!
set(value) {
attributes["href"] = value
}
}
fun html(init: HTML.() -> Unit): HTML {
val html = HTML()
html.init()
return html
}
它构建一个 HTML 树。代码中大量使用了扩展函数和带有接收者的 lambda 表达式,学会它也能帮助我们更加的熟悉Kotlin写出更加优雅的代码。使用方法如下:
fun result() =
html {
head {
title {
+"XML encoding with Kotlin"
h1 {
+"111111111"
}
}
}
body {
h1 { +"XML encoding with Kotlin" }
p { +"this format can be used as an alternative markup to XML" }
// 一个具有属性和文本内容的元素
a(href = "https://kotlinlang.org") { +"Kotlin" }
// 混合的内容
p {
+"This is some"
b { +"mixed" }
+"text. For more see the"
a(href = "https://kotlinlang.org") { +"Kotlin" }
+"project"
}
p { +"some text" }
}
}
fun main() {
println(result().toString())
}