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

name.remal.gradle_plugins.dsl.utils.code_quality.FindBugsReport-readXml.kt Maven / Gradle / Ivy

There is a newer version: 1.9.2
Show newest version
package name.remal.gradle_plugins.dsl.utils.code_quality

import name.remal.asString
import name.remal.clearNamespaces
import name.remal.nullIfEmpty
import name.remal.setNoOpEntityResolver
import name.remal.setNoValidatingXMLReaderFactory
import org.jdom2.Document
import org.jdom2.Element
import org.jdom2.input.SAXBuilder
import org.jdom2.located.Located
import java.io.File
import java.io.InputStream
import java.io.Reader
import java.net.URL

fun readFindBugsReportXml(inputStream: InputStream, systemId: String? = null) = readFindBugsReportXml(newSAXBuilder().build(inputStream, systemId))
fun readFindBugsReportXml(reader: Reader, systemId: String? = null) = readFindBugsReportXml(newSAXBuilder().build(reader, systemId))
fun readFindBugsReportXml(file: File) = readFindBugsReportXml(newSAXBuilder().build(file))
fun readFindBugsReportXml(url: URL) = readFindBugsReportXml(newSAXBuilder().build(url))

fun readFindBugsReportXml(document: Document) = FindBugsReport().apply {
    if (document.rootElement?.name != "BugCollection") throw FindBugsReportParseException(document.error("Not a FindBugs/SpotBugs report"))

    val rootElement = document.clone()
        .apply { baseURI = document.baseURI }
        .clearNamespaces()
        .rootElement

    with(rootElement) {
        readStringAttr(::tool, "tool")
        readStringAttr(::version, "version")
        readLongAttr(::analysisTimestamp, "analysisTimestamp", "timestamp")
    }

    rootElement.children("Project") {
        project {
            readStringAttr(::name, "projectName")
            children("SrcDir") {
                readStringText(::srcDir)
            }
        }
    }

    rootElement.children("BugInstance") {
        bug {
            readStringAttr(::category, "category")
            readStringAttr(::type, "type")
            readIntAttr(::priority, "priority")
            readStringChildText(::message, "LongMessage")
            readStringChildText(::shortMessage, "ShortMessage")

            children("SourceLine") {
                location {
                    readStringAttr(::className, "classname")
                    readStringAttr(::sourceFile, "sourcepath", "sourcefile")
                    readIntAttr(::startLine, "start")
                    readIntAttr(::startLineOffset, "startOffset")
                    readIntAttr(::endLine, "end")
                    readIntAttr(::endLineOffset, "endOffset")
                }
            }
        }
    }

    rootElement.children("BugPattern") {
        val name = getAttributeValue("type")?.nullIfEmpty() ?: return@children
        type(name) {
            readStringChildText(::htmlDescription, "Details")
        }
    }

    rootElement.children("BugCategory") {
        val name = getAttributeValue("category")?.nullIfEmpty() ?: return@children
        category(name) {
            readStringChildText(::htmlDescription, "Description")
        }
    }
}

class FindBugsReportParseException : RuntimeException {
    constructor() : super()
    constructor(message: String?) : super(message)
    constructor(message: String?, cause: Throwable?) : super(message, cause)
    constructor(cause: Throwable?) : super(cause)
}


private fun newSAXBuilder() = SAXBuilder().apply {
    setNoValidatingXMLReaderFactory()
    setNoOpEntityResolver()
}

private fun Element.children(name: String, callback: Element.() -> Unit) {
    getChildren(name).forEach(callback)
}

private fun Element.readStringAttr(setter: (value: String) -> Unit, vararg names: String) {
    names.forEach { name ->
        getAttributeValue(name).nullIfEmpty()?.let {
            setter(it)
            return
        }
    }
}

private fun Element.readLongAttr(setter: (value: Long) -> Unit, vararg names: String) {
    names.forEach { name ->
        getAttributeValue(name).nullIfEmpty()?.toLongOrNull()?.let {
            setter(it)
            return
        }
    }
}

private fun Element.readIntAttr(setter: (value: Int) -> Unit, vararg names: String) {
    names.forEach { name ->
        getAttributeValue(name).nullIfEmpty()?.toIntOrNull()?.let {
            setter(it)
            return
        }
    }
}

private fun Element.readStringChildText(setter: (value: String) -> Unit, vararg names: String) {
    names.forEach { name ->
        val text = getChild(name)?.text?.trim('\n', '\r') ?: return@forEach
        if (text.trim().isEmpty()) return@forEach

        setter(text)
        return
    }
}

private fun Element.readStringText(setter: (value: String) -> Unit) {
    val text = text?.trim('\n', '\r') ?: return
    if (text.trim().isEmpty()) return

    setter(text)
}

private fun Document.error(message: String) = buildString {
    append(message)
    append(": ")
    val baseURI = baseURI
    if (baseURI.isNullOrEmpty()) {
        append(asString(true))
    } else {
        append(baseURI)
    }
}

private fun Element.error(message: String) = buildString {
    append(message)
    append(": ")
    val baseURI = document?.baseURI
    if (baseURI.isNullOrEmpty()) {
        append(asString(true))
    } else {
        append(baseURI)
        if (this@error is Located) {
            append(": (").append(line).append(", ").append(column).append(')')
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy