aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src/main/kotlin/parsers/HtmlParser.kt
blob: ece3cf2403106b825ee86f494047e6a135008966 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package org.jetbrains.dokka.base.parsers

import org.jetbrains.dokka.model.doc.*
import org.jetbrains.dokka.base.parsers.factories.DocTagsFromStringFactory
import org.jsoup.Jsoup
import org.jsoup.nodes.Node
import org.jsoup.select.NodeFilter
import org.jsoup.select.NodeTraversor

class HtmlParser : Parser() {

    inner class NodeFilterImpl : NodeFilter {

        private val nodesCache: MutableMap<Int, MutableList<DocTag>> = mutableMapOf()
        private var currentDepth = 0

        fun collect(): DocTag = nodesCache[currentDepth]!![0]

        override fun tail(node: Node?, depth: Int): NodeFilter.FilterResult {
            val nodeName = node!!.nodeName()
            val nodeAttributes = node.attributes()

            if(nodeName in listOf("#document", "html", "head"))
                return NodeFilter.FilterResult.CONTINUE

            val body: String
            val params: Map<String, String>


            if(nodeName != "#text") {
                body = ""
                params = nodeAttributes.map { it.key to it.value }.toMap()
            } else {
                body = nodeAttributes["#text"]
                params = emptyMap()
            }

            val docNode = if(depth < currentDepth) {
                DocTagsFromStringFactory.getInstance(nodeName, nodesCache.getOrDefault(currentDepth, mutableListOf()).toList(), params, body).also {
                    nodesCache[currentDepth] = mutableListOf()
                    currentDepth = depth
                }
            } else {
                DocTagsFromStringFactory.getInstance(nodeName, emptyList(), params, body)
            }

            nodesCache.getOrDefault(depth, mutableListOf()) += docNode
            return NodeFilter.FilterResult.CONTINUE
        }

        override fun head(node: Node?, depth: Int): NodeFilter.FilterResult {

            val nodeName = node!!.nodeName()

            if(currentDepth < depth) {
                currentDepth = depth
                nodesCache[currentDepth] = mutableListOf()
            }

            if(nodeName in listOf("#document", "html", "head"))
                return NodeFilter.FilterResult.CONTINUE

            return NodeFilter.FilterResult.CONTINUE
        }
    }


    private fun htmlToDocNode(string: String): DocTag {
        val document = Jsoup.parse(string)
        val nodeFilterImpl = NodeFilterImpl()
        NodeTraversor.filter(nodeFilterImpl, document.root())
        return nodeFilterImpl.collect()
    }

    private fun replaceLinksWithHrefs(javadoc: String): String = Regex("\\{@link .*?}").replace(javadoc) {
        val split = it.value.dropLast(1).split(" ")
        if(split.size !in listOf(2, 3))
            return@replace it.value
        if(split.size == 3)
            return@replace "<documentationlink href=\"${split[1]}\">${split[2]}</documentationlink>"
        else
            return@replace "<documentationlink href=\"${split[1]}\">${split[1]}</documentationlink>"
    }

    override fun parseStringToDocNode(extractedString: String) = htmlToDocNode(extractedString)
    override fun preparse(text: String) = replaceLinksWithHrefs(text)
}