/**
* 领域特定语言DSL
* 概念:只在特定领域使用的语言,如:HTML、Gradle、SQL等等
* 特点:1.是计算机编程语言
* 2.具有语言的表达能力
* 3.有限的表达能力
* 4.关注某个特定的领域语言
*/
interface Node { //节点接口
fun render(): String
}
class MapDelegate(val map: MutableMap<String, String>) :
ReadWriteProperty<Any, String> { //map代理
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
}
}
/**
* 此处的参数block表示一个(参数为Tag的拓展函数、无返回值的)函数
*/
fun html(block: Tag.() -> Unit): Tag { //简化html标签的函数
// return Tag("html").apply { block(this) } //此返回值可直接使用Tag标签内的环境
//此返回值可直接使用Tag标签内的环境(效果与上一句注释掉的相同)
return Tag("html").apply(block)
}
fun Tag.head(block: Head.() -> Unit) { //简化head标签的函数
return this@head + Head().apply(block) //此返回值可直接使用Head标签内的环境
}
fun Tag.body(block: Body.() -> Unit) { //简化body标签的函数
return this@body + Body().apply(block) //此返回值可直接使用Body标签内的环境
}
class StringNode(val content: String): Node { //简化标签中添加文字的函数
override fun render() = content
}
class Head: Tag("head")
class Body: Tag("body") {
var id by MapDelegate(properties)
var `class` by MapDelegate(properties)
}
open class Tag(val name: String): Node { //标签类
val children = ArrayList<Node>() //子节点列表
val properties = HashMap<String, String>() //属性
//字符串拓展方法,使用方式 "属性名"("属性值") ,如:"id"("htmlId")
operator fun String.invoke(value: String) {
properties[this] = value //this表示字符串本身,value表示构造器中传入的值
}
//字符串拓展方法,使用方式 "属性名"("属性值") ,如:"head"{ }
//此处的参数block表示一个(参数为Tag的拓展函数、无返回值的)函数
operator fun String.invoke(block: Tag.() -> Unit) {
children.add(Tag(this).apply(block)) //使用apply调用block函数后返回Tag自身
}
//字符串拓展方法,一元运算符重载
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 StringBuilder()
.append("<") //添加左尖括号
.append(name) //添加标签的名字
.let { stringBuilder -> //对StringBuilder进行html节点的设置
if (!this.properties.isEmpty()) { //当标签属性不为空
stringBuilder.append(" ") //添加空格
this.properties.forEach { //遍历属性
stringBuilder.append(it.key) //添加属性名
.append("=\"") //添加等于号和左引号
.append(it.value) //添加属性的值
.append("\" ") //加添右引号和空格
}
}
stringBuilder //将设置完成后的stringBuilder作为返回值
}
.append(">") //添加右尖括号
.let { stringBuilder -> //对StringBuilder进行子节点的设置
children.map(Node::render) //将子节点转换成Node类型的列表
.map(stringBuilder::append) //用append方法把Node类型的列表连接起来
stringBuilder //将设置完成后的stringBuilder作为返回值
}
.append("</$name>") //添加右边标签信息
.toString() //将stringBuilder转换成字符串
}
}
fun main(args: Array<String>) {
Tag("html").apply { //生成网页方法1
properties["id"] = "HtmlId"
children.add(Tag("head"))
}.render().let(::println)
html { //生成网页方法2,此方法直接在函数html()中返回了Tag("Html").apply
properties["id"] = "HtmlId"
children.add(Tag("head"))
}.render().let(::println)
html { //生成网页方法3,此方法用字符串的拓展函数简化了标签的设置
"id"("HtmlId")
"head" {
"id"("headId")
}
body {
id = "bodyId"
`class` = "bodyClass"
"a" {
"href"("https://www.qq.com")
+"腾讯网"
}
}
}.render().let(::println)
}
运行结果