diff --git a/utils/spdx/src/main/kotlin/SpdxExpression.kt b/utils/spdx/src/main/kotlin/SpdxExpression.kt index 24069a3c2a8ac..45104948ee27d 100644 --- a/utils/spdx/src/main/kotlin/SpdxExpression.kt +++ b/utils/spdx/src/main/kotlin/SpdxExpression.kt @@ -113,6 +113,11 @@ sealed class SpdxExpression { */ abstract fun normalize(mapDeprecated: Boolean = true): SpdxExpression + /** + * Return a simplified expression that has e.g. redundancies removed. + */ + open fun simplify(): SpdxExpression = this + /** * Return this expression sorted lexicographically. */ @@ -244,6 +249,21 @@ class SpdxCompoundExpression( override fun normalize(mapDeprecated: Boolean) = SpdxCompoundExpression(operator, children.map { it.normalize(mapDeprecated) }) + override fun simplify(): SpdxExpression { + val flattenedChildren = children.flatMapTo(mutableSetOf()) { child -> + val simplifiedChild = child.simplify() + + if (simplifiedChild is SpdxCompoundExpression && simplifiedChild.operator == operator) { + // Inline nested children of the same operator. + simplifiedChild.children.map { it.simplify() } + } else { + setOf(simplifiedChild) + } + } + + return flattenedChildren.singleOrNull() ?: SpdxCompoundExpression(operator, flattenedChildren) + } + override fun sorted(): SpdxExpression { /** * Get all transitive children of this expression that are concatenated with the same operator as this compound diff --git a/utils/spdx/src/test/kotlin/SpdxCompoundExpressionTest.kt b/utils/spdx/src/test/kotlin/SpdxCompoundExpressionTest.kt index 8de3ba0b320bd..7c3f93479fc41 100644 --- a/utils/spdx/src/test/kotlin/SpdxCompoundExpressionTest.kt +++ b/utils/spdx/src/test/kotlin/SpdxCompoundExpressionTest.kt @@ -21,6 +21,7 @@ package org.ossreviewtoolkit.utils.spdx import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.WordSpec +import io.kotest.matchers.shouldBe class SpdxCompoundExpressionTest : WordSpec({ "Creating a compound expression" should { @@ -34,4 +35,49 @@ class SpdxCompoundExpressionTest : WordSpec({ } } } + + "Simplifying a compound expression" should { + "inline nested children of the same operator" { + val expression = SpdxCompoundExpression( + SpdxOperator.AND, + listOf( + SpdxCompoundExpression( + SpdxOperator.AND, + listOf( + SpdxLicenseIdExpression("MIT"), + SpdxCompoundExpression( + SpdxOperator.AND, + listOf( + SpdxLicenseIdExpression("MIT"), + SpdxLicenseIdExpression("Apache-2.0") + ) + ) + ) + ), + SpdxLicenseIdExpression("Apache-2.0") + ) + ) + + // Compare string representations to not rely on semantic equality. + expression.simplify().toString() shouldBe SpdxCompoundExpression( + SpdxOperator.AND, + listOf( + SpdxLicenseIdExpression("MIT"), + SpdxLicenseIdExpression("Apache-2.0") + ) + ).toString() + } + + "create a single expression for equal operands" { + val expression = SpdxCompoundExpression( + SpdxOperator.AND, + listOf( + SpdxLicenseIdExpression("MIT"), + SpdxLicenseIdExpression("MIT") + ) + ) + + expression.simplify() shouldBe SpdxLicenseIdExpression("MIT") + } + } })