name.remal.gradle_plugins.dsl.utils.code_quality.FindBugsReport-readXml.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-plugins-kotlin-dsl Show documentation
Show all versions of gradle-plugins-kotlin-dsl Show documentation
Remal Gradle plugins: gradle-plugins-kotlin-dsl
package name.remal.gradle_plugins.dsl.utils.code_quality
import name.remal.*
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(')')
}
}
}