All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.bestwu.apidoc.HtmlGenerator.kt Maven / Gradle / Ivy

There is a newer version: 1.2.71
Show newest version
package cn.bestwu.apidoc

import org.pegdown.*
import org.pegdown.ast.AnchorLinkNode
import org.pegdown.ast.InlineHtmlNode
import org.pegdown.ast.TableCellNode
import org.pegdown.ast.TextNode
import org.pegdown.plugins.PegDownPlugins
import org.pegdown.plugins.ToHtmlSerializerPlugin
import java.io.File
import java.io.StringWriter


/**
 * 生成接口文档
 *
 * @author Peter Wu
 */
object HtmlGenerator {

    fun call(apidocExtension: ApidocExtension) {
        val pmdFiles = if (apidocExtension.sourceFile.exists())
            apidocExtension.sourceFile.listFiles { file: File -> file.name.endsWith(".md") }
        else
            emptyArray()

        val subDirNameMap = mutableMapOf()
        apidocExtension.pathNames.forEach {
            val split = it.split(":")
            subDirNameMap[split[0]] = split[1]
        }

        val subDirs = apidocExtension.sourceFile.listFiles { file -> file.isDirectory }
        val subDirNames = subDirs.map { it.name }
        subDirNames.forEach {
            if (!subDirNameMap.contains(it)) {
                subDirNameMap[it] = it
            }
        }
        subDirs.forEach {
            if (it.exists()) {
                val extraFiles = pmdFiles + it.listFiles { file: File ->
                    file.name.endsWith(".md")
                }
                val currentDir = it.name


                call(apidocExtension, File(apidocExtension.outputFile, "$currentDir/md"), File(apidocExtension.outputFile, "$currentDir/html"), subDirNameMap - currentDir, subDirNameMap[currentDir], *extraFiles)
            }
        }
    }

    private fun call(apidocExtension: ApidocExtension, input: File, output: File, subDirNames: Map, currentDirName: String?, vararg extraFiles: File) {
        output.deleteRecursively()
        if (!output.exists()) {
            output.mkdirs()
        }


        val readme = extraFiles.find { it.name == "README.md" }
        val catalogOut = StringWriter()
        val projectName = apidocExtension.projectName
        if (readme != null) {
            catalogOut.appendln("###   [$projectName($currentDirName)](index.html) ###")
            catalogOut.appendln("")
            catalogOut.appendln("- [系统介绍](index.html)")
        }
        catalogOut.appendln("")
        extraFiles.forEach { file ->
            val name = file.name
            if (name != "README.md")
                catalogOut.appendln("- [${name.replace(".md", "")}](${name.replace(".md", ".html")})")
        }

        subDirNames.forEach { k,v->
            catalogOut.appendln("- [$v](../../$k/html)")
        }

        catalogOut.appendln("")
        catalogOut.appendln("---")
        catalogOut.appendln("")

        val listOfFiles = mutableListOf()
        if (input.exists()) {
            val files = input.listFiles()
            files.sort()
            listOfFiles.addAll(files)
            files.forEach {
                val fileName = it.name.replace(".md", "")
                if (apidocExtension.collapsible) {
                    val aClass = if (apidocExtension.expanded) "expanded" else "collapsed"
                    catalogOut.appendln("- ${fileName.replace(Regex("^0?(.*)$"), "$1").replace("-", " ")}")
                } else {
                    catalogOut.appendln("- [${fileName.replace(Regex("^0?(.*)$"), "$1").replace("-", " ")}]($fileName.html#${getAnchor(fileName).replace(Regex("^0?(.*)$"), "$1")})")
                }

                it.forEachLine { l ->
                    val reg = Regex("^ *(#+).*#?$")
                    if (l.matches(reg) && l.replace(reg, "$1").length <= 4) {
                        val name = l.replace(Regex("^ *#+ *(.*?) *#*$"), "$1")
                        if (name != fileName.replace(Regex("^0?(.*)$"), "$1").replace("-", " ")) {
                            catalogOut.appendln("\t- [$name]($fileName.html#${getAnchor(name)})")
                        }
                    }
                }
                catalogOut.appendln("")
                catalogOut.appendln("---")
            }
        }

        val catalog = catalogOut.toString()

        listOfFiles.addAll(extraFiles)
        listOfFiles.forEach {
            markdown2html(catalog, it, File(output, "${if (it.name == "README.md") "index" else it.name.replace(".md", "")}.html"))
        }
    }

    private fun getAnchor(name: String): String = AnchorLinkNode(name).name

    private fun markdown2html(catalog: String, inFile: File, outFile: File) {
        val fileName = inFile.name.replace(".md", "")
        var title = fileName.replace(Regex("^0?(.*)$"), "$1").replace("-", " ")
        if ("README" == title) {
            title = "接口文档"
        }
        val catalogHtml = PegDownProcessor(Extensions.ALL_WITH_OPTIONALS xor Extensions.SMARTYPANTS).markdownToHtml(catalog)
        val content = MdProcessor(Extensions.ALL_WITH_OPTIONALS xor Extensions.SMARTYPANTS).markdownToHtml(inFile.readText().replace("README.md", "index.html").replace(".md", ".html"))
        outFile.writeText(HtmlGenerator::class.java.getResource("/template.html").readText().format(title, catalogHtml, content))
        println("生成:$outFile")
    }


    class MdProcessor(options: Int) : PegDownProcessor(options, PegDownProcessor.DEFAULT_MAX_PARSING_TIME, PegDownPlugins.NONE) {

        override fun markdownToHtml(markdownSource: CharArray?, linkRenderer: LinkRenderer?, verbatimSerializerMap: MutableMap?, plugins: MutableList?): String? {
            try {
                val asRoot = parseMarkdown(markdownSource)
                return object : ToHtmlSerializer(linkRenderer, verbatimSerializerMap, plugins) {
                    private var index = 0

                    override fun visit(node: AnchorLinkNode) {
                        if (!node.name.matches(Regex("\\d.*"))) {
                            super.visit(AnchorLinkNode(index.toString() + node.name, node.name))
                            index++
                        } else
                            super.visit(node)
                    }

                    override fun visit(node: InlineHtmlNode) {
                        super.visit(InlineHtmlNode(node.text.replace("href='html/", "href='")))
                    }

                    override fun visit(node: TableCellNode) {
                        val me = this
                        val tag = if (me.inTableHeader) "th" else "td"
                        val columns = me.currentTableNode.columns
                        val column = columns[Math.min(me.currentTableColumn, columns.size - 1)]

                        me.printer.println().print("<").print(tag).print(" title=\"")
                        var titleText = ""
                        node.children.filter { it is TextNode }.forEach {
                            titleText += (it as TextNode).text.replace("\"", """).replace("href='html/", "href='")
                        }
                        me.printer.print(titleText)
                        me.printer.print("\"")
                        column.accept(me)
                        if (node.colSpan > 1) me.printer.print(" colspan=\"").print(Integer.toString(node.colSpan)).print("\"")
                        me.printer.print(">")
                        visitChildren(node)
                        me.printer.print("<").print("/").print(tag).print(">")

                        me.currentTableColumn += node.colSpan
                    }
                }.toHtml(asRoot)
            } catch (ignored: ParsingTimeoutException) {
                return null
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy