This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/master by this push:
new 09fdd6a6e8 Fix ConditionParser to handle newlines before && operator
(#12038)
09fdd6a6e8 is described below
commit 09fdd6a6e841c30a73adf26ac9b715c286f3bf99
Author: Blasius Patrick <[email protected]>
AuthorDate: Sat Jun 13 17:02:25 2026 +0700
Fix ConditionParser to handle newlines before && operator (#12038)
* Fix ConditionParser to handle newlines before && operator
Fix for https://github.com/apache/maven/issues/11882
The tokenize() method only treated space as whitespace, so newlines
became standalone tokens. When a line break appeared before &&,
the operator was parsed as two separate & tokens instead of one &&.
Added \n, \r, \t to whitespace handling and properly tokenize &&
as a single operator token regardless of surrounding whitespace.
Signed-off-by: Blasius Patrick <[email protected]>
* Update
impl/maven-impl/src/main/java/org/apache/maven/impl/model/profile/ConditionParser.java
Co-authored-by: Guillaume Nodet <[email protected]>
* Update
impl/maven-impl/src/main/java/org/apache/maven/impl/model/profile/ConditionParser.java
Co-authored-by: Guillaume Nodet <[email protected]>
* Update
impl/maven-impl/src/test/java/org/apache/maven/impl/model/profile/ConditionParserTest.java
Co-authored-by: Guillaume Nodet <[email protected]>
* fix: handle || operator and remove & from X= lookahead
Address review feedback from gnodet on PR #12038:
- Remove c == '&' from the >=/<= /==/!= lookahead (&= is not valid)
- Add || tokenization symmetrically with &&
- Add regression tests for || with line breaks
Fixes apache/maven#11882
Signed-off-by: Blasius Patrick <[email protected]>
---------
Signed-off-by: Blasius Patrick <[email protected]>
Co-authored-by: Guillaume Nodet <[email protected]>
---
.../maven/impl/model/profile/ConditionParser.java | 21 ++++++++--
.../impl/model/profile/ConditionParserTest.java | 48 ++++++++++++++++++++++
2 files changed, 66 insertions(+), 3 deletions(-)
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/profile/ConditionParser.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/profile/ConditionParser.java
index f596946aa2..2f6a075f87 100644
---
a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/profile/ConditionParser.java
+++
b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/profile/ConditionParser.java
@@ -129,18 +129,33 @@ private List<String> tokenize(String expression) {
}
quoteType = c;
sb.append(c);
- } else if (c == ' ' || c == '(' || c == ')' || c == ',' || c ==
'+' || c == '>' || c == '<' || c == '='
- || c == '!') {
+ } else if (Character.isWhitespace(c)
+ || c == '('
+ || c == ')'
+ || c == ','
+ || c == '+'
+ || c == '>'
+ || c == '<'
+ || c == '='
+ || c == '!'
+ || c == '&'
+ || c == '|') {
if (!sb.isEmpty()) {
tokens.add(sb.toString());
sb.setLength(0);
}
- if (c != ' ') {
+ if (!Character.isWhitespace(c)) {
if ((c == '>' || c == '<' || c == '=' || c == '!')
&& i + 1 < expression.length()
&& expression.charAt(i + 1) == '=') {
tokens.add(c + "=");
i++; // Skip the next character
+ } else if (c == '&' && i + 1 < expression.length() &&
expression.charAt(i + 1) == '&') {
+ tokens.add("&&");
+ i++; // Skip the next character
+ } else if (c == '|' && i + 1 < expression.length() &&
expression.charAt(i + 1) == '|') {
+ tokens.add("||");
+ i++; // Skip the next character
} else {
tokens.add(String.valueOf(c));
}
diff --git
a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/profile/ConditionParserTest.java
b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/profile/ConditionParserTest.java
index 56d2cbb3ae..990af0d7e3 100644
---
a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/profile/ConditionParserTest.java
+++
b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/profile/ConditionParserTest.java
@@ -262,6 +262,54 @@ void testPropertyAlias() {
assertThrows(RuntimeException.class, () -> parser.parse("${unclosed"));
}
+ @Test
+ void testAmpersandAmpersandTokenizerMultiline() {
+ // Regression test for https://github.com/apache/maven/issues/11882
+ // The && operator was not being tokenized correctly when a line break
appeared before it.
+ // Uses ${os.name} and ${os.arch} which are set to 'windows' and
'amd64' in the mock context.
+
+ // Case 1: Basic && without line breaks (baseline - always worked)
+ assertTrue((Boolean) parser.parse("${os.arch} == 'amd64' && ${os.name}
== 'windows'"));
+
+ // Case 2: Line break BEFORE && - this was the bug from issue #11882
+ // In the issue, CDATA content had a line break before &&:
+ // <condition><![CDATA[exists( '.profile-2' )\n&& missing(
'.profile-1' )]]></condition>
+ assertTrue((Boolean) parser.parse("${os.arch} == 'amd64'\n&&
${os.name} == 'windows'"));
+
+ // Case 3: Line break AFTER &&
+ assertTrue((Boolean) parser.parse("${os.arch} == 'amd64'
&&\n${os.name} == 'windows'"));
+
+ // Case 4: Line breaks on both sides
+ assertTrue((Boolean) parser.parse("${os.arch} ==
'amd64'\n&&\n${os.name} == 'windows'"));
+
+ // Case 5: Multiple && with line break before first && (like
bad-profile-2d in issue)
+ assertTrue(
+ (Boolean) parser.parse("${os.arch} == 'amd64'\n&& ${os.name}
== 'windows' && ${os.name} == 'windows'"));
+ }
+
+ @Test
+ void testPipePipeTokenizerMultiline() {
+ // Regression test for https://github.com/apache/maven/issues/11882
+ // The || operator was not being tokenized correctly when a line break
appeared before it.
+ // Uses ${os.name} which is set to 'windows' in the mock context.
+
+ // Case 1: Basic || without line breaks (baseline)
+ assertTrue((Boolean) parser.parse("${os.arch} == 'amd64' || ${os.name}
== 'windows'"));
+
+ // Case 2: Line break BEFORE ||
+ assertTrue((Boolean) parser.parse("${os.arch} == 'amd64'\n||
${os.name} == 'windows'"));
+
+ // Case 3: Line break AFTER ||
+ assertTrue((Boolean) parser.parse("${os.arch} == 'amd64'
||\n${os.name} == 'windows'"));
+
+ // Case 4: Line breaks on both sides
+ assertTrue((Boolean) parser.parse("${os.arch} ==
'amd64'\n||\n${os.name} == 'windows'"));
+
+ // Case 5: Mixed && and || with line breaks
+ assertTrue(
+ (Boolean) parser.parse("${os.arch} == 'amd64'\n&& ${os.name}
== 'windows' || ${os.name} == 'windows'"));
+ }
+
@Test
void testNestedPropertyAlias() {
functions.put("property", args -> {