-
Notifications
You must be signed in to change notification settings - Fork 314
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Apply license choices to SPDX expressions #3425
Apply license choices to SPDX expressions #3425
Conversation
143e47f
to
93b2c38
Compare
93b2c38
to
7f63032
Compare
e590394
to
7e24729
Compare
* Applies a license [choice] to the given SPDX [subtreeMatcher]. | ||
* The [subtreeMatcher] must not contain more than two choices. | ||
*/ | ||
open fun applyChoice(subtreeMatcher: SpdxExpression, choice: SpdxExpression): SpdxExpression { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand the API here, why is choice
not applied to this
but to subtreeMatcher
? And why must it not contain more than two choices?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is
choice
not applied tothis
but tosubtreeMatcher
?
Because the choice might only be a choice of a single subtree. E.g. (a OR b) AND c.
In my implementation the user would choose b
for the subtree a OR b
and the result would be a AND c
.
Another approach would be to not use subtree matchers at all. E.g. if the choice is b
it will be chosen no matter if the expression is a OR b
, a OR b OR c
or (a OR b) AND c
.
I had the impression from our conversation and from #2610 that this is the wanted behavior.
And why must it not contain more than two choices?
Because I thought that a OR b OR c
should be handled in two separate choices:
a OR b
chooseb
b OR c
chooseb
Allowing three choices would end in even more combinations that need to be covered:a OR b
chooseb
b OR c
chooseb
a OR b OR c
choosec
If you see a use case that this separation is required I'd agree to remove this limitation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH, I had a different understanding of how choices should work. In my view, a valid choice should always be checked against the whole expression. There should be no such thing as choices on sub-expressions.
That is, if the original expression would be (a OR b) AND c
, then b
would not be a valid choice, as it omits c
. The only valid choices would be a AND c
or b AND c
.
Similarly, the only valid choices for a OR b OR c
would be a
, b
or c
. There should not be any "intermediate" choices like a OR b
, and after applying a choice, the resulting expression must not contain OR
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mnonnenmacher do you agree with @sschuberth?
I think this would make the API and the implementation easier and the configuration for users more straight forward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH, I'm a bit surprised that making choices for sub-trees is being questioned again, as we discussed this in the meeting and I believe it became clear that it should be supported.
Picking up Sebastian's example (a OR b) AND c
I believe the choice (a OR b) -> b
should be applicable to it and resolve it to `b AND c´.
That is, I believe the model of a choice should be a tuple (expression, target-expression)
whereas given any input expression X it would replace all subtrees of X which are equivalant to expression
with target-expression
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I'm simply confused about the API, even after looking at the tests, mostly due to variable naming I believe. In particular I do not like the term "subtreeMatcher", but prefer "subExpression".
I can see a value of being able to first "narrow down" the choice, and incrementally doing that to get a final choice which does not contain an OR
anymore.
What would help at least me for a better understanding would be to change the function as follows:
/**
* Apply a license [choice], optionally limited to the given [subExpression], and return an [SpdxExpression] where
* the choice is resolved.
*/
open fun applyChoice(choice: SpdxExpression, subExpression: SpdxExpression = this): SpdxExpression {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And why must it not contain more than two choices?
Because I thought that
a OR b OR c
should be handled in two separate choices:
IMO, ideally the API would work in a way that I can generically say "whenever I come across sub-expression X, choose Y", so I should be able to say "whenever I come across sub-expression a OR b OR c
, choose b
" or also "whenever I come across sub-expression a OR b OR c
, choose b OR c
".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, ideally the API would work in a way that I can generically say "whenever I come across sub-expression X, choose Y", so I should be able to say "whenever I come across sub-expression
a OR b OR c
, chooseb
" or also "whenever I come across sub-expressiona OR b OR c
, chooseb OR c
".
Fully agree, that seem 100% inline with what I envisioned as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the API to your suggestions and tried to improve the readability.
Please also have a look at the tests that cover everything that I thought of. If a test doesn't exist I forgot that specific use case.
Short summary how the API will behave:
- Whenever I come across sub-expression
a OR b OR c
, chooseb
- Whenever I come across sub-expression
a OR b OR c
, chooseb OR c
- If the sub-expression does not match the expression, the expression is returned (should we use
- Throws an exception if the choice is not valid for the subExpression
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fviernau, @sschuberth please have another look at the changes
7e24729
to
c0a5aaf
Compare
c0a5aaf
to
ad5a0fc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm basically fine with the formal aspects of the PR now, but I'd like @mnonnenmacher to approve on the API.
ad5a0fc
to
809f0b5
Compare
d7394ec
to
ff77dae
Compare
773604a
to
c3dc5de
Compare
c3dc5de
to
5bdafe5
Compare
5cfbac4
to
2d61fa4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fviernau @mnonnenmacher I believe this is the API we agreed on and now finally ready. Please also do a re-review so we're sure you're fine with the API and we can take it into use without expecting it to change again soon. Thanks in advance!
override fun applyChoice(choice: SpdxExpression, subExpression: SpdxExpression): SpdxExpression { | ||
if (!subExpression.validChoices().containsAll(choice.validChoices())) { | ||
throw InvalidLicenseChoiceException( | ||
"$choice is not a valid choice for the subtree expression. Valid choices are: ${validChoices()}." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"subexpression" instead of "subtree expression", and I think it would be good to also print the subexpression in this message. Or maybe just say "...is not a valid choice for $subExpression. ..."
return replaceMatcherWithChoice(subExpression, choice) | ||
} | ||
|
||
private fun replaceMatcherWithChoice(subExpression: SpdxExpression, choice: SpdxExpression): SpdxExpression { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is "matcher" a left-over from a renaming? Maybe rename the function to replaceSubexpressionWithChoice
.
|
||
private fun replaceMatcherWithChoice(subExpression: SpdxExpression, choice: SpdxExpression): SpdxExpression { | ||
val expressionString = toString() | ||
val matcherString = subExpression.toString() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, rename to subexpressionString
?
@@ -516,3 +558,5 @@ enum class SpdxOperator( | |||
*/ | |||
OR(0) | |||
} | |||
|
|||
class InvalidLicenseChoiceException(message: String) : Exception(message) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to extend SpdxException
instead?
val expression = "a OR b OR c".toSpdx() | ||
val choice = "a".toSpdx() | ||
|
||
val result = expression.applyChoice(choice) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently this is the same test as "apply the choice if the expression contains multiple choices" below, according to the test name I think you wanted to pass a subexpression here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was a leftover from before the API change where not more than one choice were allowed. As now choices can be applied I removed this test.
result shouldBe "b".toSpdx() | ||
} | ||
|
||
"throw an exception if the chosen license is not an valid option" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: "a valid"
val choice = "x".toSpdx() | ||
val subExpression = "x OR y OR z".toSpdx() | ||
|
||
val result = expression.applyChoice(choice, subExpression) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't it throw an exception if the subexpression is invalid like for invalid license choices?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I agree that this would make the API more consistent and will fail early if the developer uses this API in another way as it was intended.
I'm mostly fine with the API and had only minor comments. The only API change I mentioned in my comments is if we should throw an exception if |
2d61fa4
to
4bcfa51
Compare
To be able to apply license choices to SpdxCompoundExpressions, a logic is added to apply the user's choices to a specified matcher. Signed-off-by: Marcel Bochtler <marcel.bochtler@bosch.io>
4bcfa51
to
fe23cd3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also need @mnonnenmacher to re-review and approve.
Merging this despite the stuck markdown-link-check as all other checks passed. |
This is a draft how license choices should be applied.
The signature of the tested function looks like this:
fun applyChoice(choice: SpdxExpression, subtreeMatcher: SpdxExpression = this): SpdxExpression
See #3396 and #2610