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 f596946aa253..948ffe88edf2 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,21 @@ private List 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 ((c == '>' || c == '<' || c == '=' || c == '!') + if (!Character.isWhitespace(c)) { + if ((c == '>' || 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 { 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 56d2cbb3aec9..11f7bd28a754 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,31 @@ 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 &&: + // + 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 testNestedPropertyAlias() { functions.put("property", args -> {