diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java
index 2ed439d37505..f76a4d15ea1c 100644
--- a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java
+++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java
@@ -42,7 +42,7 @@
*
unlimited number of version components,
* version components in the text can be digits or strings,
* strings are checked for well-known qualifiers and the qualifier ordering is used for version ordering.
- * Well-known qualifiers (case insensitive) are:
+ * Well-known qualifiers (case-insensitive) are:
* alpha or a
* beta or b
* milestone or m
@@ -51,9 +51,9 @@
* (the empty string) or ga or final
* sp
*
- * Unknown qualifiers are considered after known qualifiers, with lexical order (always case insensitive),
+ * Unknown qualifiers are considered after known qualifiers, with lexical order (always case-insensitive),
*
- * a hyphen usually precedes a qualifier, and is always less important than digits/number, for example
+ * A hyphen usually precedes a qualifier, and is always less important than digits/number. For example
* {@code 1.0.RC2 < 1.0-RC3 < 1.0.1}; but prefer {@code 1.0.0-RC1} over {@code 1.0.0.RC1}, and more
* generally: {@code 1.0.X2 < 1.0-X3 < 1.0.1} for any string {@code X}; but prefer {@code 1.0.0-X1}
* over {@code 1.0.0.X1}.
@@ -656,7 +656,20 @@ public final void parseVersion(String version) {
int startIndex = 0;
for (int i = 0; i < version.length(); i++) {
- char c = version.charAt(i);
+ char character = version.charAt(i);
+ int c = character;
+ if (Character.isHighSurrogate(character)) {
+ // read the next character as a low surrogate and combine into a single int
+ try {
+ char low = version.charAt(i + 1);
+ char[] both = {character, low};
+ c = Character.codePointAt(both, 0);
+ i++;
+ } catch (IndexOutOfBoundsException ex) {
+ // high surrogate without low surrogate. Not a lot we can do here except treat it as a regular
+ // character
+ }
+ }
if (c == '.') {
if (i == startIndex) {
@@ -687,7 +700,7 @@ public final void parseVersion(String version) {
stack.push(list);
}
isCombination = false;
- } else if (Character.isDigit(c)) {
+ } else if (c >= '0' && c <= '9') { // Check for ASCII digits only
if (!isDigit && i > startIndex) {
// X1
isCombination = true;
diff --git a/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionTest.java b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionTest.java
index d7616405bdd2..219d760bab64 100644
--- a/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionTest.java
+++ b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionTest.java
@@ -27,7 +27,6 @@
/**
* Test ComparableVersion.
- *
*/
@SuppressWarnings("unchecked")
class ComparableVersionTest {
@@ -222,6 +221,23 @@ void testLeadingZeroes() {
checkVersionsOrder("0.2", "1.0.7");
}
+ @Test
+ void testDigitGreaterThanNonAscii() {
+ ComparableVersion c1 = new ComparableVersion("1");
+ ComparableVersion c2 = new ComparableVersion("é");
+ assertTrue(c1.compareTo(c2) > 0, "expected " + "1" + " > " + "\uD835\uDFE4");
+ assertTrue(c2.compareTo(c1) < 0, "expected " + "\uD835\uDFE4" + " < " + "1");
+ }
+
+ @Test
+ void testDigitGreaterThanNonBmpCharacters() {
+ ComparableVersion c1 = new ComparableVersion("1");
+ // MATHEMATICAL SANS-SERIF DIGIT TWO
+ ComparableVersion c2 = new ComparableVersion("\uD835\uDFE4");
+ assertTrue(c1.compareTo(c2) > 0, "expected " + "1" + " > " + "\uD835\uDFE4");
+ assertTrue(c2.compareTo(c1) < 0, "expected " + "\uD835\uDFE4" + " < " + "1");
+ }
+
@Test
void testGetCanonical() {
// MNG-7700
@@ -238,13 +254,25 @@ void testGetCanonical() {
@Test
void testCompareDigitToLetter() {
- ComparableVersion c1 = new ComparableVersion("7");
- ComparableVersion c2 = new ComparableVersion("J");
- ComparableVersion c3 = new ComparableVersion("c");
- assertTrue(c1.compareTo(c2) > 0, "expected 7 > J");
- assertTrue(c2.compareTo(c1) < 0, "expected J < 1");
- assertTrue(c1.compareTo(c3) > 0, "expected 7 > c");
- assertTrue(c3.compareTo(c1) < 0, "expected c < 7");
+ ComparableVersion seven = new ComparableVersion("7");
+ ComparableVersion capitalJ = new ComparableVersion("J");
+ ComparableVersion lowerCaseC = new ComparableVersion("c");
+ // Digits are greater than letters
+ assertTrue(seven.compareTo(capitalJ) > 0, "expected 7 > J");
+ assertTrue(capitalJ.compareTo(seven) < 0, "expected J < 1");
+ assertTrue(seven.compareTo(lowerCaseC) > 0, "expected 7 > c");
+ assertTrue(lowerCaseC.compareTo(seven) < 0, "expected c < 7");
+ }
+
+ @Test
+ void testNonAsciiDigits() { // These should not be treated as digits.
+ ComparableVersion asciiOne = new ComparableVersion("1");
+ ComparableVersion arabicEight = new ComparableVersion("\u0668");
+ ComparableVersion asciiNine = new ComparableVersion("9");
+ assertTrue(asciiOne.compareTo(arabicEight) > 0, "expected " + "1" + " > " + "\u0668");
+ assertTrue(arabicEight.compareTo(asciiOne) < 0, "expected " + "\u0668" + " < " + "1");
+ assertTrue(asciiNine.compareTo(arabicEight) > 0, "expected " + "9" + " > " + "\u0668");
+ assertTrue(arabicEight.compareTo(asciiNine) < 0, "expected " + "\u0668" + " < " + "9");
}
@Test