kotlin 高阶函数 DSL 手撕HTML

kotlin 高阶函数 DSL 解析HTML


前言

使用纯代码 加 注释的方式,可以更快的理解源码
如果你喜欢,请点个赞,后期会不断的深入讲解


一、kotlin 高阶函数 解析 HTML

先看一张图

在这里插入图片描述

代码如下

import java.io.File

//   面向对象思维, 设计 类的关系,(依赖倒转原则)面向对象 而 不面向细节
private interface Element {
    
            // 元素顶级接口

    /**
     * buffer    拼接所有元素
     * indent    缩进效果
     */
    fun run(builder: StringBuilder, indent: String)
}

// 从最简单的 text 文本开始
private class TextElement(val text: String) : Element {
    
    

    override fun run(builder: StringBuilder, indent: String) {
    
    
//        缩进后,显示文本内容
        builder.append("$indent$text\n")
    }
}

//    开始写复杂的部分  <html>  <head>
private open class Tag(val tagName: String) : Element {
    
          //tagName 文本的 Element 如 <html>  <head>

    //    集合,每一个元素都是 Element
    val elements = arrayListOf<Element>()

    //    Map 集合, 每一个元素 key = 元素名, Value = 属性对应的值
    val attribute = hashMapOf<String, String>()

    override fun run(builder: StringBuilder, indent: String) {
    
         // 被 toString触发
//        缩进<html 属性> <head>
        builder.append("$indent<$tagName${
      
      renderAttributes()}>\n")

        for (element in elements) {
    
    
            element.run(builder, indent + "  ")
        }

//        </html>  闭合
        builder.append("$indent</$tagName>\n")
    }

    //    属性拼装  href = "https://blog.csdn.net/u010755471"
//    null   没有属性
    private fun renderAttributes(): String? {
    
    
        val builder = StringBuilder()
        for (key in attribute.keys) {
    
    
//            空格: <a href
            builder.append(" $key=\"${
      
      attribute[key]}\"")
        }

        return builder.toString()
    }


    override fun toString(): String {
    
    

        val stringBuilder = StringBuilder()

        run(stringBuilder, "")     // 参数1: stringBuilder 方便组装HTML 参数,  参数二:indent 不要缩进

        return stringBuilder.toString()
    }


}

private open class TagClass(tagName: String) : Tag(tagName) {
    
    

    operator fun String.unaryPlus() {
    
           // 运算符重载 +
        elements.add(TextElement(this))  // elements.add
    }

    operator fun String.unaryMinus() {
    
           // 运算符重载 -
        elements += TextElement(this)
    }
}


//   第一个中转站 Html
private class Html : TagClass("html") {
    
    

    fun head(action: Head.() -> Unit) {
    
    
        val newHead = Head()
        newHead.action()

        elements += newHead      //  添加进数组
    }

    fun body(action: Body.() -> Unit) {
    
    
        val newBody = Body("body")
        newBody.action()

        elements += newBody
    }
}

//   Head 中转站
private class Head : TagClass("head") {
    
    
    fun title(action: Title.() -> Unit) {
    
    
        val newTitle = Title()
        newTitle.action()

        elements.add(newTitle)
    }
}

//    Body 中转站
private open class Body(tagName: String) : TagClass(tagName = tagName) {
    
    

    fun h1(action: H1.() -> Unit) {
    
    
        val newH1 = H1()
        newH1.action()

        elements += newH1
    }

    fun p(action: P.() -> Unit) {
    
    
        val newP = P()
        newP.action()

        elements += newP
    }

    open fun a(href: String, action: A.() -> Unit) {
    
    

        val newA = A()
        newA.href = href
        newA.action()

        elements += newA
    }
}

//    h1 中转站
private class H1 : Body("h1") {
    
    

}

//     P 中转站
private class P : Body("p") {
    
    
    fun b(action: B.() -> Unit) {
    
    
        val newB = B()
        newB.action()

        elements += newB
    }

    override fun a(href: String, action: A.() -> Unit) {
    
    
        val newA = A()
        newA.href = href
        newA.action()

        elements += newA
    }

    fun ul(action: Ul.() -> Unit) {
    
    
        val newUl = Ul()
        newUl.action()

        elements += newUl
    }
}


//     A 中转站
private class A : Body("a") {
    
    
    var href: String
        get() = attribute["href"]!!
        set(value) {
    
    
            attribute["href"] = value
        }

}

//     P 中转站
private class B : Body("b") {
    
    

}

//     Ul 中转站
private class Ul : Body("ul") {
    
    
    fun li(action: Li.() -> Unit) {
    
    
        val newLi = Li()
        newLi.action()

        elements.add(newLi)
    }
}

//     Li 中转站
private class Li : Body("li") {
    
    }

//     Title 中转站
private class Title : TagClass("title") {
    
    

}


private fun html(action: Html.() -> Unit): Html {
    
    
    val html = Html()
    html.action()

    return html
}

fun main(args: Array<String>) {
    
    

    val names = listOf("张三", "大漂亮", "王美丽")

    val result =
        html {
    
     // this == 第一个中转站 { head body 。。 }
            head {
    
     // this == head中转站 { title }
                title {
    
     +"使用 Kotlin 进行 HTML 编码" }
            }
            body {
    
     // this == body中转站 { h1 p a p }
                h1 {
    
     // this == h1中转站 { 未知 }
                }
                p {
    
     -"此格式可用作 HTML 的替代标记" }

                // 具有属性和文本内容的元素
                a(href = "https://blog.csdn.net/u010755471") {
    
     -"不爱学习的猪的博客" }

                // 混合内容
                p {
    
    
                    -"Derry老师来了"
                    b {
    
     -"Derry是谁" }
                    -"文本。有关更多信息,请参阅"
                    a(href = "https://blog.csdn.net/u010755471") {
    
     -"不爱学习的猪的博客" }
                    -"Derry的项目"
                }
                p {
    
     -"一些文字" }

                // 从命令行参数生成的内容
                p {
    
    
                    -"命令行参数是:"
                    ul {
    
     // this == UL中转站 { li 子标签  }
                        for (name in names)
                            li {
    
     -name } // this == LI中转站
                    }
                }
            }
        }
    println(result)

    val file = File("/Users/Documents/Android leaning work/kotlinleaning04/testHtml.html")
    file.writeText(result.toString())
}

二、使用 DSL 解析HTML

先来张图

在这里插入图片描述

源码如下:

import java.io.File

//   定义一个节点接口
interface Node {
    
    
    fun create(): String
}

// 中转站
class BlockNode(val name: String) : Node {
    
    

    val children = ArrayList<Node>()     // 节点集合:  html head body
    private val properties = hashMapOf<String, Any>()   //属性集合:style='color: white; font-family: Microsoft YaHei'

    override fun create(): String {
    
    
        return """<$name ${
    
    properties.map {
    
     "${
      
      it.key}='${
      
      it.value}'" }.joinToString(" ")}>${
    
    children.joinToString ( "" ){
    
    it.create()}}</$name"""
    }

    operator fun String.invoke(action: BlockNode.() -> Unit){
    
    
        val stringNode = BlockNode(this)
        stringNode.action()

        this@BlockNode.children += stringNode
    }

    operator fun String.invoke(value: Any){
    
    
        this@BlockNode.properties[this] = value
    }

    operator fun String.unaryPlus(){
    
    
        val stringNode = StringNode("$this &sbsp; &sbsp;")

        this@BlockNode.children += stringNode
    }

}

class StringNode(private val value: String) : Node {
    
    
    override fun create(): String {
    
    
        return value
    }
}

fun html(action: BlockNode.() -> Unit): BlockNode {
    
    
    val blockNode = BlockNode("html")
    blockNode.action()

    return blockNode
}

fun BlockNode.head(action: BlockNode.() -> Unit) {
    
    
    val head = BlockNode("head")
    head.action()

    children += head
}

fun BlockNode.body(action: BlockNode.() -> Unit) {
    
    
    val body = BlockNode("body")
    body.action()

    children += body
}

fun main() {
    
    
    val htmlContent = html {
    
     // this持有中转站BlockNode
        head {
    
     // this持有中转站BlockNode

            // String.invoke(Any)
            "meta" {
    
     "charset"("UTF-8") }
        }
        body {
    
    
            "div" {
    
    
                "style"(
                    """
                    width: 666px; 
                    height: 666px; 
                    line-height: 600px; 
                    background-color: #F00;
                    text-align: center
                    """.trimIndent()
                )
                "span" {
    
    
                    "style"(
                        """
                        color: white;
                        font-family: Microsoft YaHei
                        """.trimIndent()
                    )
                    +"你好 HTML DSL!!"
                    +"我就是我,不一样的烟火"
                    +"像我这样牛逼的人"
                    +"世界上还有几人"
                }
            }
        }
    }.create() // 用户调用create函数,我就组装

    println(htmlContent)

    File("/Users/tiger/Documents/Android leaning work/kotlinleaning04/ttt.html")
        .writeText(htmlContent)
}

猜你喜欢

转载自blog.csdn.net/u010755471/article/details/124290213