Skip to content

Commit

Permalink
✨ support XML parser
Browse files Browse the repository at this point in the history
  • Loading branch information
XiYang6666 committed Jul 23, 2024
1 parent a2edb3b commit 9a10c49
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 3 deletions.
1 change: 0 additions & 1 deletion Mitori-Satori/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ dependencies {
implementation("io.ktor:ktor-server-status-pages:2.3.12")
implementation("io.ktor:ktor-server-content-negotiation:2.3.12")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.12")

}

tasks.test {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package xyz.xasmc.mitori.satori.util.xmlParser

class PlainTextElement(val text: String) : XmlElement("text", emptyMap(), emptyList()) {
override fun toString(): String {
return text
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package xyz.xasmc.mitori.satori.util.xmlParser

class XmlDocument(
val elements: List<XmlElement>,
) {
override fun toString() = elements.joinToString("")

companion object {
fun fromString(str: String): XmlDocument {
return XmlDocument(parseStr(str).first)
}

private fun parseStr(content: String, parentName: String? = null): Pair<MutableList<XmlElement>, Int> {
val result = mutableListOf<XmlElement>()
val sb = StringBuilder()
var ptr = 0
while (ptr < content.length) {
// 处理闭标签
if (content[ptr] == '<' && content[ptr + 1] == '/') {
if (sb.isNotEmpty()) {
result.add(PlainTextElement(sb.toString().trimEnd()))
sb.clear()
}
val endIndex = content.indexOf(">", ptr)
val closeTagStr = content.substring(ptr..endIndex)
val closeTagName = closeTagStr.removeSurrounding("</", ">").trim()
if (closeTagName == parentName) return Pair(result, endIndex + 1) // 正确的闭标签, 返回
throw IllegalArgumentException("Unknown CloseTag: $closeTagStr")
}
// 处理开标签
if (content[ptr] == '<') {
if (sb.isNotEmpty()) {
result.add(PlainTextElement(sb.toString().trimEnd()))
sb.clear()
}
val endIndex = content.indexOf(">", ptr)
if (content[endIndex - 1] == '/') {
// 立即关闭
val openTagStr = content.substring(ptr..endIndex)
val (name, attr) = parseOpenTag(openTagStr)
result.add(XmlElement(name, attr, mutableListOf()))
ptr = endIndex + 1
continue
}
val openTagStr = content.substring(ptr..endIndex)
val (name, attr) = parseOpenTag(openTagStr)
// 处理剩下的内容
ptr = endIndex + 1
val contentStr = content.substring(ptr..<content.length)
val (child, read) = parseStr(contentStr, name)
ptr += read
result.add(XmlElement(name, attr, child))
continue
}
// 啥也不是,原始文本
if (sb.isNotEmpty() || (content[ptr] != '\n' && content[ptr] != ' ')) if (content[ptr] == '\n') {
result.add(PlainTextElement(sb.toString().trimEnd()))
sb.clear()
result.add(XmlElement("br", emptyMap(), emptyList()))
} else sb.append(content[ptr])
ptr++
}
return Pair(result, content.length)
}

private fun parseOpenTag(str: String): Pair<String, MutableMap<String, String>> {
val regex = Regex("""^< *([a-zA-Z0-9]+)(?: ((?: *[a-zA-Z0-9]+(?: *= *"[^"]*")? *)*))? */?>${'$'}""")
val result = regex.find(str) ?: throw IllegalArgumentException("Can not parse tag: $str")
val name = result.groupValues[1]

val attr = mutableMapOf<String, String>()
val attrStr = result.groupValues[2]
val attrRegex = Regex("""([a-zA-Z0-9]+)(?: *= *"([^"]+)")?""")
attrRegex.findAll(attrStr).forEach {
val key = it.groupValues[1]
val value = it.groupValues[2]
attr[key] = value
}
return Pair(name, attr)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package xyz.xasmc.mitori.satori.util.xmlParser

open class XmlElement(
val name: String,
val attributes: Map<String, String>,
val children: List<XmlElement>,
) {
override fun toString(): String {
val sb = StringBuilder()
sb.append("<").append(name)
attributes.forEach {
sb.append(" ").append(it.key)
if (it.value.isNotEmpty()) sb.append("=\"").append(it.value).append("\"")
}
if (children.isNotEmpty()) {
sb.append(">")
children.forEach { sb.append(it.toString()) }
sb.append("</").append(name).append(">")
} else {
sb.append("/>")
}
return sb.toString()
}
}
26 changes: 26 additions & 0 deletions Mitori-Satori/src/test/kotlin/XmlParserTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import org.junit.jupiter.api.Test
import xyz.xasmc.mitori.satori.util.xmlParser.XmlDocument
import kotlin.test.assertEquals

class XmlParserTest {
@Test
fun testXmlParser() {
val result = XmlDocument.fromString(
"""
<a href="awa" aaa>aLink</a>
1145144<br/>
1919810
awawawaw
<p>
awa
<a href="/login">login</a>
qwqwqwq
</p>
""".trimIndent()
)
assertEquals(
result.toString(),
"<a href=\"awa\" aaa>aLink</a>1145144<br/>1919810<br/>awawawaw<br/><p>awa<br/><a href=\"/login\">login</a>qwqwqwq<br/></p>"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ class MitoriVelocity {
val playerUuid = player.uniqueId
val firstJoin = playerJoinTime[playerUuid] == null
playerJoinTime.computeIfAbsent(playerUuid) { Instant.now() }
// 处理事件
if (!firstJoin) return
// 构建事件
val now = Instant.now()
val satoriMember = PlayerUtil.createSatoriMember(player, now.toEpochMilli())
val satoriUser = PlayerUtil.createSatoriUser(player)
Expand All @@ -86,7 +86,7 @@ class MitoriVelocity {
val playerUuid = player.uniqueId
if (!playerJoinTime.containsKey(playerUuid)) return
playerJoinTime.remove(playerUuid)
// 处理事件
// 构建事件
val now = Instant.now()
val satoriMember = PlayerUtil.createSatoriMember(player, now.toEpochMilli())
val satoriUser = PlayerUtil.createSatoriUser(player)
Expand Down

0 comments on commit 9a10c49

Please sign in to comment.