|
|
|
|
@ -17,27 +17,26 @@
@@ -17,27 +17,26 @@
|
|
|
|
|
package org.springframework.security.core; |
|
|
|
|
|
|
|
|
|
import java.math.BigInteger; |
|
|
|
|
import java.util.ArrayDeque; |
|
|
|
|
import java.util.ArrayList; |
|
|
|
|
import java.util.Arrays; |
|
|
|
|
import java.util.Deque; |
|
|
|
|
import java.util.Iterator; |
|
|
|
|
import java.util.List; |
|
|
|
|
import java.util.ListIterator; |
|
|
|
|
import java.util.Locale; |
|
|
|
|
import java.util.Properties; |
|
|
|
|
import java.util.Stack; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* <p> |
|
|
|
|
* Generic implementation of version comparison. |
|
|
|
|
* </p> |
|
|
|
|
* |
|
|
|
|
* NOTE: This is a copy from |
|
|
|
|
* https://svn.apache.org/repos/asf/maven/maven-3/tags/maven-3.1.0
|
|
|
|
|
* /maven-artifact/src/main/ |
|
|
|
|
* java/org/apache/maven/artifact/versioning/ComparableVersion.java |
|
|
|
|
* https://github.com/apache/maven/blob/maven-3.6.3/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java
|
|
|
|
|
* |
|
|
|
|
* <p> |
|
|
|
|
* Features: |
|
|
|
|
* <ul> |
|
|
|
|
* <li>mixing of '<code>-</code>' (dash) and '<code>.</code>' (dot) separators,</li> |
|
|
|
|
* <li>mixing of '<code>-</code>' (hyphen) and '<code>.</code>' (dot) separators,</li> |
|
|
|
|
* <li>transition between characters and digits also constitutes a separator: |
|
|
|
|
* <code>1.0alpha1 => [1, 0, alpha, 1]</code></li> |
|
|
|
|
* <li>unlimited number of version components,</li> |
|
|
|
|
@ -55,19 +54,22 @@ import java.util.Stack;
@@ -55,19 +54,22 @@ import java.util.Stack;
|
|
|
|
|
* </ul> |
|
|
|
|
* Unknown qualifiers are considered after known qualifiers, with lexical order (always |
|
|
|
|
* case insensitive),</li> |
|
|
|
|
* <li>a dash usually precedes a qualifier, and is always less important than something |
|
|
|
|
* <li>a hyphen usually precedes a qualifier, and is always less important than something |
|
|
|
|
* preceded with a dot.</li> |
|
|
|
|
* </ul> |
|
|
|
|
* </p> |
|
|
|
|
* |
|
|
|
|
* @author Kenney Westerhof |
|
|
|
|
* @author Hervé Boutemy |
|
|
|
|
* @see <a href= |
|
|
|
|
* "https://cwiki.apache.org/confluence/display/MAVENOLD/Versioning">"Versioning" on Maven |
|
|
|
|
* Wiki</a> |
|
|
|
|
* @author <a href="mailto:kenney@apache.org">Kenney Westerhof</a> |
|
|
|
|
* @author <a href="mailto:hboutemy@apache.org">Hervé Boutemy</a> |
|
|
|
|
*/ |
|
|
|
|
class ComparableVersion implements Comparable<ComparableVersion> { |
|
|
|
|
|
|
|
|
|
private static final int MAX_INTITEM_LENGTH = 9; |
|
|
|
|
|
|
|
|
|
private static final int MAX_LONGITEM_LENGTH = 18; |
|
|
|
|
|
|
|
|
|
private String value; |
|
|
|
|
|
|
|
|
|
private String canonical; |
|
|
|
|
@ -76,7 +78,11 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -76,7 +78,11 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
|
|
|
|
|
private interface Item { |
|
|
|
|
|
|
|
|
|
int INTEGER_ITEM = 0; |
|
|
|
|
int INT_ITEM = 3; |
|
|
|
|
|
|
|
|
|
int LONG_ITEM = 4; |
|
|
|
|
|
|
|
|
|
int BIGINTEGER_ITEM = 0; |
|
|
|
|
|
|
|
|
|
int STRING_ITEM = 1; |
|
|
|
|
|
|
|
|
|
@ -91,43 +97,194 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -91,43 +97,194 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Represents a numeric item in the version item list. |
|
|
|
|
* Represents a numeric item in the version item list that can be represented with an |
|
|
|
|
* int. |
|
|
|
|
*/ |
|
|
|
|
private static class IntegerItem implements Item { |
|
|
|
|
private static class IntItem implements Item { |
|
|
|
|
|
|
|
|
|
private static final BigInteger BigInteger_ZERO = new BigInteger("0"); |
|
|
|
|
private final int value; |
|
|
|
|
|
|
|
|
|
private final BigInteger value; |
|
|
|
|
public static final IntItem ZERO = new IntItem(); |
|
|
|
|
|
|
|
|
|
private IntItem() { |
|
|
|
|
this.value = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IntItem(String str) { |
|
|
|
|
this.value = Integer.parseInt(str); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public int getType() { |
|
|
|
|
return INT_ITEM; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean isNull() { |
|
|
|
|
return value == 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public int compareTo(Item item) { |
|
|
|
|
if (item == null) { |
|
|
|
|
return (value == 0) ? 0 : 1; // 1.0 == 1, 1.1 > 1
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch (item.getType()) { |
|
|
|
|
case INT_ITEM: |
|
|
|
|
int itemValue = ((IntItem) item).value; |
|
|
|
|
return (value < itemValue) ? -1 : ((value == itemValue) ? 0 : 1); |
|
|
|
|
case LONG_ITEM: |
|
|
|
|
case BIGINTEGER_ITEM: |
|
|
|
|
return -1; |
|
|
|
|
|
|
|
|
|
case STRING_ITEM: |
|
|
|
|
return 1; // 1.1 > 1-sp
|
|
|
|
|
|
|
|
|
|
case LIST_ITEM: |
|
|
|
|
return 1; // 1.1 > 1-1
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
throw new IllegalStateException("invalid item: " + item.getClass()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean equals(Object o) { |
|
|
|
|
if (this == o) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if (o == null || getClass() != o.getClass()) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IntItem intItem = (IntItem) o; |
|
|
|
|
|
|
|
|
|
return value == intItem.value; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public int hashCode() { |
|
|
|
|
return value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public String toString() { |
|
|
|
|
return Integer.toString(value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Represents a numeric item in the version item list that can be represented with a |
|
|
|
|
* long. |
|
|
|
|
*/ |
|
|
|
|
private static class LongItem implements Item { |
|
|
|
|
|
|
|
|
|
private final long value; |
|
|
|
|
|
|
|
|
|
LongItem(String str) { |
|
|
|
|
this.value = Long.parseLong(str); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public int getType() { |
|
|
|
|
return LONG_ITEM; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean isNull() { |
|
|
|
|
return value == 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public int compareTo(Item item) { |
|
|
|
|
if (item == null) { |
|
|
|
|
return (value == 0) ? 0 : 1; // 1.0 == 1, 1.1 > 1
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch (item.getType()) { |
|
|
|
|
case INT_ITEM: |
|
|
|
|
return 1; |
|
|
|
|
case LONG_ITEM: |
|
|
|
|
long itemValue = ((LongItem) item).value; |
|
|
|
|
return (value < itemValue) ? -1 : ((value == itemValue) ? 0 : 1); |
|
|
|
|
case BIGINTEGER_ITEM: |
|
|
|
|
return -1; |
|
|
|
|
|
|
|
|
|
case STRING_ITEM: |
|
|
|
|
return 1; // 1.1 > 1-sp
|
|
|
|
|
|
|
|
|
|
public static final IntegerItem ZERO = new IntegerItem(); |
|
|
|
|
case LIST_ITEM: |
|
|
|
|
return 1; // 1.1 > 1-1
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
throw new IllegalStateException("invalid item: " + item.getClass()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean equals(Object o) { |
|
|
|
|
if (this == o) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if (o == null || getClass() != o.getClass()) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private IntegerItem() { |
|
|
|
|
this.value = BigInteger_ZERO; |
|
|
|
|
LongItem longItem = (LongItem) o; |
|
|
|
|
|
|
|
|
|
return value == longItem.value; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public int hashCode() { |
|
|
|
|
return (int) (value ^ (value >>> 32)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IntegerItem(String str) { |
|
|
|
|
@Override |
|
|
|
|
public String toString() { |
|
|
|
|
return Long.toString(value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Represents a numeric item in the version item list. |
|
|
|
|
*/ |
|
|
|
|
private static class BigIntegerItem implements Item { |
|
|
|
|
|
|
|
|
|
private final BigInteger value; |
|
|
|
|
|
|
|
|
|
BigIntegerItem(String str) { |
|
|
|
|
this.value = new BigInteger(str); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public int getType() { |
|
|
|
|
return INTEGER_ITEM; |
|
|
|
|
return BIGINTEGER_ITEM; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean isNull() { |
|
|
|
|
return BigInteger_ZERO.equals(this.value); |
|
|
|
|
return BigInteger.ZERO.equals(value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public int compareTo(Item item) { |
|
|
|
|
if (item == null) { |
|
|
|
|
return BigInteger_ZERO.equals(this.value) ? 0 : 1; // 1.0 == 1, 1.1 > 1
|
|
|
|
|
return BigInteger.ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch (item.getType()) { |
|
|
|
|
case INTEGER_ITEM: |
|
|
|
|
return this.value.compareTo(((IntegerItem) item).value); |
|
|
|
|
case INT_ITEM: |
|
|
|
|
case LONG_ITEM: |
|
|
|
|
return 1; |
|
|
|
|
|
|
|
|
|
case BIGINTEGER_ITEM: |
|
|
|
|
return value.compareTo(((BigIntegerItem) item).value); |
|
|
|
|
|
|
|
|
|
case STRING_ITEM: |
|
|
|
|
return 1; // 1.1 > 1-sp
|
|
|
|
|
@ -136,13 +293,32 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -136,13 +293,32 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
return 1; // 1.1 > 1-1
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
throw new RuntimeException("invalid item: " + item.getClass()); |
|
|
|
|
throw new IllegalStateException("invalid item: " + item.getClass()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean equals(Object o) { |
|
|
|
|
if (this == o) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if (o == null || getClass() != o.getClass()) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
BigIntegerItem that = (BigIntegerItem) o; |
|
|
|
|
|
|
|
|
|
return value.equals(that.value); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public int hashCode() { |
|
|
|
|
return value.hashCode(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public String toString() { |
|
|
|
|
return this.value.toString(); |
|
|
|
|
return value.toString(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
@ -152,14 +328,14 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -152,14 +328,14 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
*/ |
|
|
|
|
private static class StringItem implements Item { |
|
|
|
|
|
|
|
|
|
private static final String[] QUALIFIERS = { "alpha", "beta", "milestone", "rc", "snapshot", "", "sp" }; |
|
|
|
|
|
|
|
|
|
private static final List<String> _QUALIFIERS = Arrays.asList(QUALIFIERS); |
|
|
|
|
private static final List<String> QUALIFIERS = Arrays.asList("alpha", "beta", "milestone", "rc", "snapshot", "", |
|
|
|
|
"sp"); |
|
|
|
|
|
|
|
|
|
private static final Properties ALIASES = new Properties(); |
|
|
|
|
static { |
|
|
|
|
ALIASES.put("ga", ""); |
|
|
|
|
ALIASES.put("final", ""); |
|
|
|
|
ALIASES.put("release", ""); |
|
|
|
|
ALIASES.put("cr", "rc"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -168,9 +344,9 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -168,9 +344,9 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
* determine if a given qualifier makes the version older than one without a |
|
|
|
|
* qualifier, or more recent. |
|
|
|
|
*/ |
|
|
|
|
private static final String RELEASE_VERSION_INDEX = String.valueOf(_QUALIFIERS.indexOf("")); |
|
|
|
|
private static final String RELEASE_VERSION_INDEX = String.valueOf(QUALIFIERS.indexOf("")); |
|
|
|
|
|
|
|
|
|
private String value; |
|
|
|
|
private final String value; |
|
|
|
|
|
|
|
|
|
StringItem(String value, boolean followedByDigit) { |
|
|
|
|
if (followedByDigit && value.length() == 1) { |
|
|
|
|
@ -185,6 +361,7 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -185,6 +361,7 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
case 'm': |
|
|
|
|
value = "milestone"; |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
this.value = ALIASES.getProperty(value, value); |
|
|
|
|
@ -197,7 +374,7 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -197,7 +374,7 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean isNull() { |
|
|
|
|
return (comparableQualifier(this.value).compareTo(RELEASE_VERSION_INDEX) == 0); |
|
|
|
|
return (comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX) == 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -214,35 +391,56 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -214,35 +391,56 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
* @return an equivalent value that can be used with lexical comparison |
|
|
|
|
*/ |
|
|
|
|
public static String comparableQualifier(String qualifier) { |
|
|
|
|
int i = _QUALIFIERS.indexOf(qualifier); |
|
|
|
|
int i = QUALIFIERS.indexOf(qualifier); |
|
|
|
|
|
|
|
|
|
return i == -1 ? (_QUALIFIERS.size() + "-" + qualifier) : String.valueOf(i); |
|
|
|
|
return i == -1 ? (QUALIFIERS.size() + "-" + qualifier) : String.valueOf(i); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public int compareTo(Item item) { |
|
|
|
|
if (item == null) { |
|
|
|
|
// 1-rc < 1, 1-ga > 1
|
|
|
|
|
return comparableQualifier(this.value).compareTo(RELEASE_VERSION_INDEX); |
|
|
|
|
return comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX); |
|
|
|
|
} |
|
|
|
|
switch (item.getType()) { |
|
|
|
|
case INTEGER_ITEM: |
|
|
|
|
case INT_ITEM: |
|
|
|
|
case LONG_ITEM: |
|
|
|
|
case BIGINTEGER_ITEM: |
|
|
|
|
return -1; // 1.any < 1.1 ?
|
|
|
|
|
|
|
|
|
|
case STRING_ITEM: |
|
|
|
|
return comparableQualifier(this.value).compareTo(comparableQualifier(((StringItem) item).value)); |
|
|
|
|
return comparableQualifier(value).compareTo(comparableQualifier(((StringItem) item).value)); |
|
|
|
|
|
|
|
|
|
case LIST_ITEM: |
|
|
|
|
return -1; // 1.any < 1-1
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
throw new RuntimeException("invalid item: " + item.getClass()); |
|
|
|
|
throw new IllegalStateException("invalid item: " + item.getClass()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean equals(Object o) { |
|
|
|
|
if (this == o) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if (o == null || getClass() != o.getClass()) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
StringItem that = (StringItem) o; |
|
|
|
|
|
|
|
|
|
return value.equals(that.value); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public int hashCode() { |
|
|
|
|
return value.hashCode(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public String toString() { |
|
|
|
|
return this.value; |
|
|
|
|
return value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
@ -264,12 +462,14 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -264,12 +462,14 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void normalize() { |
|
|
|
|
for (ListIterator<Item> iterator = listIterator(size()); iterator.hasPrevious();) { |
|
|
|
|
Item item = iterator.previous(); |
|
|
|
|
if (item.isNull()) { |
|
|
|
|
iterator.remove(); // remove null trailing items: 0, "", empty list
|
|
|
|
|
for (int i = size() - 1; i >= 0; i--) { |
|
|
|
|
Item lastItem = get(i); |
|
|
|
|
|
|
|
|
|
if (lastItem.isNull()) { |
|
|
|
|
// remove null trailing items: 0, "", empty list
|
|
|
|
|
remove(i); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
else if (!(lastItem instanceof ListItem)) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -285,7 +485,9 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -285,7 +485,9 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
return first.compareTo(null); |
|
|
|
|
} |
|
|
|
|
switch (item.getType()) { |
|
|
|
|
case INTEGER_ITEM: |
|
|
|
|
case INT_ITEM: |
|
|
|
|
case LONG_ITEM: |
|
|
|
|
case BIGINTEGER_ITEM: |
|
|
|
|
return -1; // 1-1 < 1.0.x
|
|
|
|
|
|
|
|
|
|
case STRING_ITEM: |
|
|
|
|
@ -300,7 +502,7 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -300,7 +502,7 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
Item r = right.hasNext() ? right.next() : null; |
|
|
|
|
|
|
|
|
|
// if this is shorter, then invert the compare and mul with -1
|
|
|
|
|
int result = l == null ? -1 * r.compareTo(l) : l.compareTo(r); |
|
|
|
|
int result = l == null ? (r == null ? 0 : -1 * r.compareTo(l)) : l.compareTo(r); |
|
|
|
|
|
|
|
|
|
if (result != 0) { |
|
|
|
|
return result; |
|
|
|
|
@ -310,39 +512,39 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -310,39 +512,39 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
throw new RuntimeException("invalid item: " + item.getClass()); |
|
|
|
|
throw new IllegalStateException("invalid item: " + item.getClass()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public String toString() { |
|
|
|
|
StringBuilder buffer = new StringBuilder("("); |
|
|
|
|
for (Iterator<Item> iter = iterator(); iter.hasNext();) { |
|
|
|
|
buffer.append(iter.next()); |
|
|
|
|
if (iter.hasNext()) { |
|
|
|
|
buffer.append(','); |
|
|
|
|
StringBuilder buffer = new StringBuilder(); |
|
|
|
|
for (Item item : this) { |
|
|
|
|
if (buffer.length() > 0) { |
|
|
|
|
buffer.append((item instanceof ListItem) ? '-' : '.'); |
|
|
|
|
} |
|
|
|
|
buffer.append(item); |
|
|
|
|
} |
|
|
|
|
buffer.append(')'); |
|
|
|
|
return buffer.toString(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ComparableVersion(String version) { |
|
|
|
|
public ComparableVersion(String version) { |
|
|
|
|
parseVersion(version); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@SuppressWarnings("checkstyle:innerassignment") |
|
|
|
|
public final void parseVersion(String version) { |
|
|
|
|
this.value = version; |
|
|
|
|
|
|
|
|
|
this.items = new ListItem(); |
|
|
|
|
items = new ListItem(); |
|
|
|
|
|
|
|
|
|
version = version.toLowerCase(Locale.ENGLISH); |
|
|
|
|
|
|
|
|
|
ListItem list = this.items; |
|
|
|
|
ListItem list = items; |
|
|
|
|
|
|
|
|
|
Stack<Item> stack = new Stack<>(); |
|
|
|
|
Deque<Item> stack = new ArrayDeque<>(); |
|
|
|
|
stack.push(list); |
|
|
|
|
|
|
|
|
|
boolean isDigit = false; |
|
|
|
|
@ -354,7 +556,7 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -354,7 +556,7 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
|
|
|
|
|
if (c == '.') { |
|
|
|
|
if (i == startIndex) { |
|
|
|
|
list.add(IntegerItem.ZERO); |
|
|
|
|
list.add(IntItem.ZERO); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
list.add(parseItem(isDigit, version.substring(startIndex, i))); |
|
|
|
|
@ -363,30 +565,23 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -363,30 +565,23 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
} |
|
|
|
|
else if (c == '-') { |
|
|
|
|
if (i == startIndex) { |
|
|
|
|
list.add(IntegerItem.ZERO); |
|
|
|
|
list.add(IntItem.ZERO); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
list.add(parseItem(isDigit, version.substring(startIndex, i))); |
|
|
|
|
} |
|
|
|
|
startIndex = i + 1; |
|
|
|
|
|
|
|
|
|
if (isDigit) { |
|
|
|
|
list.normalize(); // 1.0-* = 1-*
|
|
|
|
|
|
|
|
|
|
if ((i + 1 < version.length()) && Character.isDigit(version.charAt(i + 1))) { |
|
|
|
|
// new ListItem only if previous were digits and new char is a
|
|
|
|
|
// digit,
|
|
|
|
|
// ie need to differentiate only 1.1 from 1-1
|
|
|
|
|
list.add(list = new ListItem()); |
|
|
|
|
|
|
|
|
|
stack.push(list); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
list.add(list = new ListItem()); |
|
|
|
|
stack.push(list); |
|
|
|
|
} |
|
|
|
|
else if (Character.isDigit(c)) { |
|
|
|
|
if (!isDigit && i > startIndex) { |
|
|
|
|
list.add(new StringItem(version.substring(startIndex, i), true)); |
|
|
|
|
startIndex = i; |
|
|
|
|
|
|
|
|
|
list.add(list = new ListItem()); |
|
|
|
|
stack.push(list); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
isDigit = true; |
|
|
|
|
@ -395,6 +590,9 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -395,6 +590,9 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
if (isDigit && i > startIndex) { |
|
|
|
|
list.add(parseItem(true, version.substring(startIndex, i))); |
|
|
|
|
startIndex = i; |
|
|
|
|
|
|
|
|
|
list.add(list = new ListItem()); |
|
|
|
|
stack.push(list); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
isDigit = false; |
|
|
|
|
@ -409,32 +607,101 @@ class ComparableVersion implements Comparable<ComparableVersion> {
@@ -409,32 +607,101 @@ class ComparableVersion implements Comparable<ComparableVersion> {
|
|
|
|
|
list = (ListItem) stack.pop(); |
|
|
|
|
list.normalize(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.canonical = this.items.toString(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static Item parseItem(boolean isDigit, String buf) { |
|
|
|
|
return isDigit ? new IntegerItem(buf) : new StringItem(buf, false); |
|
|
|
|
if (isDigit) { |
|
|
|
|
buf = stripLeadingZeroes(buf); |
|
|
|
|
if (buf.length() <= MAX_INTITEM_LENGTH) { |
|
|
|
|
// lower than 2^31
|
|
|
|
|
return new IntItem(buf); |
|
|
|
|
} |
|
|
|
|
else if (buf.length() <= MAX_LONGITEM_LENGTH) { |
|
|
|
|
// lower than 2^63
|
|
|
|
|
return new LongItem(buf); |
|
|
|
|
} |
|
|
|
|
return new BigIntegerItem(buf); |
|
|
|
|
} |
|
|
|
|
return new StringItem(buf, false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static String stripLeadingZeroes(String buf) { |
|
|
|
|
if (buf == null || buf.isEmpty()) { |
|
|
|
|
return "0"; |
|
|
|
|
} |
|
|
|
|
for (int i = 0; i < buf.length(); ++i) { |
|
|
|
|
char c = buf.charAt(i); |
|
|
|
|
if (c != '0') { |
|
|
|
|
return buf.substring(i); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return buf; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public int compareTo(ComparableVersion o) { |
|
|
|
|
return this.items.compareTo(o.items); |
|
|
|
|
return items.compareTo(o.items); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public String toString() { |
|
|
|
|
return this.value; |
|
|
|
|
return value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public String getCanonical() { |
|
|
|
|
if (canonical == null) { |
|
|
|
|
canonical = items.toString(); |
|
|
|
|
} |
|
|
|
|
return canonical; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean equals(Object o) { |
|
|
|
|
return (o instanceof ComparableVersion) && this.canonical.equals(((ComparableVersion) o).canonical); |
|
|
|
|
return (o instanceof ComparableVersion) && items.equals(((ComparableVersion) o).items); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public int hashCode() { |
|
|
|
|
return this.canonical.hashCode(); |
|
|
|
|
return items.hashCode(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// CHECKSTYLE_OFF: LineLength
|
|
|
|
|
/** |
|
|
|
|
* Main to test version parsing and comparison. |
|
|
|
|
* <p> |
|
|
|
|
* To check how "1.2.7" compares to "1.2-SNAPSHOT", for example, you can issue |
|
|
|
|
* <pre>java -jar ${maven.repo.local}/org/apache/maven/maven-artifact/${maven.version}/maven-artifact-${maven.version}.jar "1.2.7" "1.2-SNAPSHOT"</pre> |
|
|
|
|
* command to command line. Result of given command will be something like this: <pre> |
|
|
|
|
* Display parameters as parsed by Maven (in canonical form) and comparison result: |
|
|
|
|
* 1. 1.2.7 == 1.2.7 |
|
|
|
|
* 1.2.7 > 1.2-SNAPSHOT |
|
|
|
|
* 2. 1.2-SNAPSHOT == 1.2-snapshot |
|
|
|
|
* </pre> |
|
|
|
|
* @param args the version strings to parse and compare. You can pass arbitrary number |
|
|
|
|
* of version strings and always two adjacent will be compared |
|
|
|
|
*/ |
|
|
|
|
// CHECKSTYLE_ON: LineLength
|
|
|
|
|
public static void main(String... args) { |
|
|
|
|
System.out.println("Display parameters as parsed by Maven (in canonical form) and comparison result:"); |
|
|
|
|
if (args.length == 0) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ComparableVersion prev = null; |
|
|
|
|
int i = 1; |
|
|
|
|
for (String version : args) { |
|
|
|
|
ComparableVersion c = new ComparableVersion(version); |
|
|
|
|
|
|
|
|
|
if (prev != null) { |
|
|
|
|
int compare = prev.compareTo(c); |
|
|
|
|
System.out.println(" " + prev.toString() + ' ' + ((compare == 0) ? "==" : ((compare < 0) ? "<" : ">")) |
|
|
|
|
+ ' ' + version); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
System.out.println(String.valueOf(i++) + ". " + version + " == " + c.getCanonical()); |
|
|
|
|
|
|
|
|
|
prev = c; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|