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) }