From dd81d17b7c4c4d9efac84f9e18e63ff20d950f5c Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Mon, 10 Jun 2024 13:30:15 +0200 Subject: [PATCH] chore(model): Make also `readValueOrNull()` throw on multiple documents Align with `readValue()` to throw in case of multiple documents per file. `null` is only return for empty files. Signed-off-by: Sebastian Schuberth --- model/src/main/kotlin/FileFormat.kt | 22 ++++++++++------------ model/src/test/kotlin/FileFormatTest.kt | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/model/src/main/kotlin/FileFormat.kt b/model/src/main/kotlin/FileFormat.kt index faf00fb4f326..a17b7047cdb4 100644 --- a/model/src/main/kotlin/FileFormat.kt +++ b/model/src/main/kotlin/FileFormat.kt @@ -21,7 +21,6 @@ package org.ossreviewtoolkit.model import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.convertValue import com.fasterxml.jackson.module.kotlin.readValues import java.io.File @@ -98,27 +97,26 @@ fun File.readTree(): JsonNode = mapper().readTree(this) * Use the Jackson mapper returned from [File.mapper] to read a single object of type [T] from this file. Throw an * [IOException] if not exactly one value is contained in the file, e.g. in case of multiple YAML documents per file. */ -inline fun File.readValue(): T { +inline fun File.readValue(): T = + readValueOrNull() ?: throw IOException("No object found in file '$this'.") + +/** + * Use the Jackson mapper returned from [File.mapper] to read an object of type [T] from this file, or return null if + * the file has no content. Throw an [IOException] if not exactly one value is contained in the file, e.g. in case of + * multiple YAML documents per file. + */ +inline fun File.readValueOrNull(): T? { val mapper = mapper() val parser = mapper.factory.createParser(this) val values = mapper.readValues(parser).readAll().also { - if (it.isEmpty()) throw IOException("No object found in file '$this'.") + if (it.isEmpty()) return null if (it.size > 1) throw IOException("Multiple top-level objects found in file '$this'.") } return values.first() } -/** - * Use the Jackson mapper returned from [File.mapper] to read an object of type [T] from this file, or return null if - * the file has no content. - */ -inline fun File.readValueOrNull(): T? = - // Parse the file in a two-step process to avoid readValue() throwing an exception on empty files. Also see - // https://github.com/FasterXML/jackson-databind/issues/1406#issuecomment-252676674. - mapper().let { it.convertValue(it.readTree(this)) } - /** * Use the Jackson mapper returned from [File.mapper] to read an object of type [T] from this file, or return the * [default] value if the file has no content. diff --git a/model/src/test/kotlin/FileFormatTest.kt b/model/src/test/kotlin/FileFormatTest.kt index 5831615c5f0d..331c395d4cba 100644 --- a/model/src/test/kotlin/FileFormatTest.kt +++ b/model/src/test/kotlin/FileFormatTest.kt @@ -98,5 +98,27 @@ class FileFormatTest : WordSpec({ file.readValueOrNull() } } + + "refuse to read multiple documents per file" { + val file = tempfile(null, ".yml").apply { + @Suppress("MaxLineLength") + writeText( + """ + --- + id: "Maven:dom4j:dom4j:1.6.1" + source_artifact_url: "https://repo.maven.apache.org/maven2/dom4j/dom4j/1.6.1/dom4j-1.6.1-sources.jar" + --- + id: "Maven:dom4j:dom4j:1.6.1" + source_artifact_url: "/dom4j-1.6.1-sources.jar" + """.trimIndent() + ) + } + + shouldThrowWithMessage( + "Multiple top-level objects found in file '$file'." + ) { + file.readValueOrNull() + } + } } })