kotlin 高阶函数DSL 手撕HTML

举报
半身风雪 发表于 2022/06/24 11:55:14 2022/06/24
【摘要】 @[TOC](kotlin 高阶函数 DSL 解析HTML) 前言使用纯代码 加 注释的方式,可以更快的理解源码如果你喜欢,请点个赞,后期会不断的深入讲解 一、kotlin 高阶函数 解析 HTML先看一张图代码如下import java.io.File// 面向对象思维, 设计 类的关系,(依赖倒转原则)面向对象 而 不面向细节private interface Element { ...

@[TOC](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)
}

总结

🤩
🎉 原创不易,还希望各位大佬支持一下 \textcolor{blue}{原创不易,还希望各位大佬支持一下}

👍 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!}

🌟 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!}

✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!}

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。