Kotlin 进阶之路10 领域特定语言 DSL
2018-06-04 本文已影响144人
香沙小熊
DSL 的概念
- 只在特定领域使用的语言
- 例如:
- HTML、Gradle、SQL等等
DSL的特点
- 计算机编程语言
- 具有语言的表达能力
- 有限的表达能力
- 关注某个特定领域
HTML Kotlin
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class MapDelegate(val map: MutableMap<String, String>) : ReadWriteProperty<Any, String> {
override fun getValue(thisRef: Any, property: KProperty<*>): String {
return map[property.name] ?: ""
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
map[property.name] = value
}
}
interface Node {
fun render(): String
}
fun html(block: Tag.() -> Unit): Tag {
return Tag("html").apply(block)
}
fun Tag.head(block: Head.() -> Unit) {
this + Head().apply(block)
}
fun Tag.body(block: Body.() -> Unit) {
this + Body().apply(block)
}
class StringNode(val content: String) : Node {
override fun render() = content
}
class Head : Tag("head")
class Body : Tag("body") {
var id by MapDelegate(proerties)
var `class` by MapDelegate(proerties)
}
open class Tag(val name: String) : Node {
val children = ArrayList<Node>()
val proerties = HashMap<String, String>()
operator fun String.invoke(value: String) {
proerties[this] =value
}
operator fun String.invoke(block:Tag.()->Unit){
children.add(Tag(this).apply(block))
}
operator fun String.unaryPlus(){
children.add(StringNode(this))
}
operator fun plus(node: Node){
children.add(node)
}
// <html id ="htmlId' style=''><head></head><body></body></html>
override fun render(): String {
return StringBuffer()
.append("<")
.append(name)
.let { stringBuffer ->
if (!this.proerties.isEmpty()) {
stringBuffer.append(" ")
this.proerties.forEach {
stringBuffer.append(it.key)
.append("=\"")
.append(it.value)
.append("\" ")
}
}
stringBuffer
}
.append(">")
.let { stringBuffer ->
children.map(Node::render).map(stringBuffer::append)
stringBuffer
}
.append("</$name>")
.toString()
}
}
fun main(args: Array<String>) {
// Tag("html").apply {
// proerties["id"] = "HtmlId"
// children.add(Tag("head"))
//}.render().let ( ::println )
// html {
// proerties["id"] = "HtmlId"
// children.add(Tag("head"))
//
// }.render().let(::println)
html {
"id"("HtmlId") //执行 Tag中的invoke方法
"head"{
"id"("headId")
}
body {
id = "bodyId"
`class` = "bodyClass"
"a"{
"href"("https://www.kotliner.cn")
+"Kotlin 中文博客"
}
}
"div"{
}
}.render().let(::println)
}
运行结果
<html id="HtmlId" ><head id="headId" ></head><body id="bodyId" class="bodyClass" ><a href="https://www.kotliner.cn" >Kotlin 中文博客</a></body><div></div></html>
Gradle Kotlin 脚本
group = "cn.kotliner.kotlin"
version = "1.0-SNAPSHOT"
buildscript {
extra["kotlin_version"] = "1.1.2"
repositories {
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${extra["kotlin_version"]}")
}
}
apply {
plugin("java")
plugin("kotlin")
}
configure<JavaPluginConvention>{
setSourceCompatibility(1.5)
}
repositories {
mavenCentral()
}
dependencies {
compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${extra["kotlin_version"]}")
testCompile("junit", "junit", "4.12")
}