kotlin 高阶函数 DSL 手撕HTML
kotlin 高阶函数 DSL 解析HTML
文章目录
- kotlin 高阶函数 DSL 解析HTML
- 前言
- 一、kotlin 高阶函数 解析 HTML
- 二、使用 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") }}// 开始写复杂的部分 private open class Tag(val tagName: String) : Element { //tagName 文本的 Element 如 // 集合,每一个元素都是 Element val elements = arrayListOf<Element>() // Map 集合, 每一个元素 key = 元素名, Value = 属性对应的值 val attribute = hashMapOf<String, String>() override fun run(builder: StringBuilder, indent: String) { // 被 toString触发// 缩进 builder.append("$indent<$tagName${renderAttributes()}>\n") for (element in elements) { element.run(builder, indent + " ") }// 闭合 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) }}// 第一个中转站 Htmlprivate 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)}