From b8e39bf924059ac89cc96f725c0b3bbdd6edc448 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Wed, 11 Mar 2026 16:39:27 +0100 Subject: [PATCH 1/3] Feat: Improve matching of Toolchains versions This feature was totally off from what it was documented. --- .../toolchain/RequirementMatcherFactory.java | 87 +++++++++++++++---- .../RequirementMatcherFactoryTest.java | 10 ++- 2 files changed, 78 insertions(+), 19 deletions(-) diff --git a/maven-core/src/main/java/org/apache/maven/toolchain/RequirementMatcherFactory.java b/maven-core/src/main/java/org/apache/maven/toolchain/RequirementMatcherFactory.java index 4c7476bdc162..e12b5dff4ea5 100644 --- a/maven-core/src/main/java/org/apache/maven/toolchain/RequirementMatcherFactory.java +++ b/maven-core/src/main/java/org/apache/maven/toolchain/RequirementMatcherFactory.java @@ -18,9 +18,12 @@ */ package org.apache.maven.toolchain; -import org.apache.maven.artifact.versioning.DefaultArtifactVersion; -import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; -import org.apache.maven.artifact.versioning.VersionRange; +import java.util.Locale; +import java.util.Objects; + +import org.eclipse.aether.util.version.GenericVersionScheme; +import org.eclipse.aether.version.InvalidVersionSpecificationException; +import org.eclipse.aether.version.VersionScheme; /** * @@ -38,8 +41,7 @@ public static RequirementMatcher createVersionMatcher(String provideValue) { } private static final class ExactMatcher implements RequirementMatcher { - - private String provides; + private final String provides; private ExactMatcher(String provides) { this.provides = provides; @@ -47,7 +49,9 @@ private ExactMatcher(String provides) { @Override public boolean matches(String requirement) { - return provides.equalsIgnoreCase(requirement); + return Objects.equals( + provides != null ? provides.toLowerCase(Locale.ENGLISH) : null, + requirement != null ? requirement.toLowerCase(Locale.ENGLISH) : null); } @Override @@ -57,31 +61,78 @@ public String toString() { } private static final class VersionMatcher implements RequirementMatcher { - DefaultArtifactVersion version; + private final String version; private VersionMatcher(String version) { - this.version = new DefaultArtifactVersion(version); + this.version = version; } @Override public boolean matches(String requirement) { - try { - VersionRange range = VersionRange.createFromVersionSpec(requirement); - if (range.hasRestrictions()) { - return range.containsVersion(version); + String r = requirement != null ? requirement.toLowerCase(Locale.ENGLISH) : null; + String v = version != null ? version.toLowerCase(Locale.ENGLISH) : null; + if (v == null && r == null) { + return true; // null == null + } + if (v == null || r == null) { + return false; // null != non-null + } + if (v.equals(r)) { + return true; // str == str (ignoring case) + } + return matchesRequirement(v, r); + } + + private static final VersionScheme VERSION_SCHEME = new GenericVersionScheme(); + + private static boolean matchesRequirement(String version, String requirement) { + boolean interval = false; + boolean included = false; + if (requirement.endsWith("+")) { + interval = true; + included = true; + requirement = requirement.substring(0, requirement.length() - 1); + } else if (requirement.endsWith("-")) { + interval = true; + requirement = requirement.substring(0, requirement.length() - 1); + } + final String req = requirement; + + // if requirement is not a version range itself + if (!req.contains("[") && !req.contains("(") && !req.contains(",")) { + if (!interval) { + return version.startsWith(req + "."); // "11" -> "11.xxx" } else { - return range.getRecommendedVersion().compareTo(version) == 0; + try { + if (included) { + return VERSION_SCHEME + .parseVersionRange("[" + req + ",)") + .containsVersion(VERSION_SCHEME.parseVersion(version)); // "11+" -> "[11,)" + } else { + return VERSION_SCHEME + .parseVersionRange("(," + req + ")") + .containsVersion(VERSION_SCHEME.parseVersion(version)); // "11-" -> "(,11)" + } + } catch (InvalidVersionSpecificationException e) { + // nope; GenericVersionScheme never throes but we need to make compiler happy + throw new RuntimeException(e); + } + } + } else { + try { + return VERSION_SCHEME + .parseVersionRange(req) + .containsVersion(VERSION_SCHEME.parseVersion(version)); // "range" -> "range" + } catch (InvalidVersionSpecificationException e) { + // nope; GenericVersionScheme never throes but we need to make compiler happy + throw new RuntimeException(e); } - } catch (InvalidVersionSpecificationException ex) { - // TODO error reporting - ex.printStackTrace(); - return false; } } @Override public String toString() { - return version.toString(); + return version; } } } diff --git a/maven-core/src/test/java/org/apache/maven/toolchain/RequirementMatcherFactoryTest.java b/maven-core/src/test/java/org/apache/maven/toolchain/RequirementMatcherFactoryTest.java index 6b9cc2d8003d..2b68423620aa 100644 --- a/maven-core/src/test/java/org/apache/maven/toolchain/RequirementMatcherFactoryTest.java +++ b/maven-core/src/test/java/org/apache/maven/toolchain/RequirementMatcherFactoryTest.java @@ -50,13 +50,21 @@ public void testCreateExactMatcher() { public void testCreateVersionMatcher() { RequirementMatcher matcher; matcher = RequirementMatcherFactory.createVersionMatcher("1.5.2"); - assertFalse(matcher.matches("1.5")); + assertTrue(matcher.matches("1")); + assertTrue(matcher.matches("1.5")); assertTrue(matcher.matches("1.5.2")); assertFalse(matcher.matches("[1.4,1.5)")); assertFalse(matcher.matches("[1.5,1.5.2)")); assertFalse(matcher.matches("(1.5.2,1.6)")); assertTrue(matcher.matches("(1.4,1.5.2]")); assertTrue(matcher.matches("(1.5,)")); + + assertTrue(matcher.matches("1.5+")); + assertFalse(matcher.matches("1.5-")); + + assertFalse(matcher.matches("1.6+")); + assertTrue(matcher.matches("1.6-")); + assertEquals("1.5.2", matcher.toString()); // Ensure it is not printed as 1.5.0 From 31bfd36986337551619ffa715a46763cef093f9a Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Wed, 11 Mar 2026 18:55:05 +0100 Subject: [PATCH 2/3] Add UT --- .../RequirementMatcherFactoryTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/maven-core/src/test/java/org/apache/maven/toolchain/RequirementMatcherFactoryTest.java b/maven-core/src/test/java/org/apache/maven/toolchain/RequirementMatcherFactoryTest.java index 2b68423620aa..37237a487a9f 100644 --- a/maven-core/src/test/java/org/apache/maven/toolchain/RequirementMatcherFactoryTest.java +++ b/maven-core/src/test/java/org/apache/maven/toolchain/RequirementMatcherFactoryTest.java @@ -18,6 +18,10 @@ */ package org.apache.maven.toolchain; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -71,4 +75,35 @@ public void testCreateVersionMatcher() { matcher = RequirementMatcherFactory.createVersionMatcher("1.5"); assertEquals("1.5", matcher.toString()); } + + @Test + public void testCreateVersionMatcherWithJavaVersions() { + RequirementMatcher java25 = RequirementMatcherFactory.createVersionMatcher("25.0.2"); + RequirementMatcher java21 = RequirementMatcherFactory.createVersionMatcher("21.0.10"); + RequirementMatcher java17 = RequirementMatcherFactory.createVersionMatcher("17.0.18"); + RequirementMatcher java11 = RequirementMatcherFactory.createVersionMatcher("11.0.30"); + RequirementMatcher java8 = RequirementMatcherFactory.createVersionMatcher("1.8.0_482"); + List matchers = Arrays.asList(java25, java21, java17, java11, java8); + + testMatch("11", matchers, java11); + testMatch("11+", matchers, java25, java21, java17, java11); + testMatch("11-", matchers, java8); + testMatch("[11,21)", matchers, java17, java11); + testMatch("1.8", matchers, java8); + testMatch("1.8+", matchers, java25, java21, java17, java11, java8); + testMatch("8", matchers); + testMatch("8+", matchers, java25, java21, java17, java11); + } + + private static void testMatch( + String requirement, Collection allMatchers, RequirementMatcher... requiredMatchers) { + int matches = 0; + for (RequirementMatcher matcher : allMatchers) { + if (matcher.matches(requirement)) { + matches++; + assertTrue(Arrays.asList(requiredMatchers).contains(matcher)); + } + } + assertEquals(matches, requiredMatchers.length); + } } From c4b46413eb67384b976ede4d995c954f04021582 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Wed, 11 Mar 2026 20:45:09 +0100 Subject: [PATCH 3/3] Move in the logic --- .../toolchain/RequirementMatcherFactory.java | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/maven-core/src/main/java/org/apache/maven/toolchain/RequirementMatcherFactory.java b/maven-core/src/main/java/org/apache/maven/toolchain/RequirementMatcherFactory.java index e12b5dff4ea5..98114695b11e 100644 --- a/maven-core/src/main/java/org/apache/maven/toolchain/RequirementMatcherFactory.java +++ b/maven-core/src/main/java/org/apache/maven/toolchain/RequirementMatcherFactory.java @@ -86,31 +86,29 @@ public boolean matches(String requirement) { private static final VersionScheme VERSION_SCHEME = new GenericVersionScheme(); private static boolean matchesRequirement(String version, String requirement) { - boolean interval = false; - boolean included = false; - if (requirement.endsWith("+")) { - interval = true; - included = true; - requirement = requirement.substring(0, requirement.length() - 1); - } else if (requirement.endsWith("-")) { - interval = true; - requirement = requirement.substring(0, requirement.length() - 1); - } - final String req = requirement; - // if requirement is not a version range itself - if (!req.contains("[") && !req.contains("(") && !req.contains(",")) { + if (!requirement.contains("[") && !requirement.contains("(") && !requirement.contains(",")) { + boolean interval = false; + boolean included = false; + if (requirement.endsWith("+")) { + interval = true; + included = true; + requirement = requirement.substring(0, requirement.length() - 1); + } else if (requirement.endsWith("-")) { + interval = true; + requirement = requirement.substring(0, requirement.length() - 1); + } if (!interval) { - return version.startsWith(req + "."); // "11" -> "11.xxx" + return version.startsWith(requirement + "."); // "11" -> "11.xxx" } else { try { if (included) { return VERSION_SCHEME - .parseVersionRange("[" + req + ",)") + .parseVersionRange("[" + requirement + ",)") .containsVersion(VERSION_SCHEME.parseVersion(version)); // "11+" -> "[11,)" } else { return VERSION_SCHEME - .parseVersionRange("(," + req + ")") + .parseVersionRange("(," + requirement + ")") .containsVersion(VERSION_SCHEME.parseVersion(version)); // "11-" -> "(,11)" } } catch (InvalidVersionSpecificationException e) { @@ -121,7 +119,7 @@ private static boolean matchesRequirement(String version, String requirement) { } else { try { return VERSION_SCHEME - .parseVersionRange(req) + .parseVersionRange(requirement) .containsVersion(VERSION_SCHEME.parseVersion(version)); // "range" -> "range" } catch (InvalidVersionSpecificationException e) { // nope; GenericVersionScheme never throes but we need to make compiler happy