SemVer: add support for prerelease segment

GitOrigin-RevId: a34541971cfddf8bd3feb9f9437595f9f392c960
This commit is contained in:
Piotr Tomiak
2019-06-11 17:40:25 +03:00
committed by intellij-monorepo-bot
parent 7ee7b84af1
commit b1b95561d8
3 changed files with 186 additions and 29 deletions
@@ -2841,6 +2841,29 @@ public class StringUtil extends StringUtilRt {
return ignoreCase ? s1.compareToIgnoreCase(s2) : s1.compareTo(s2);
}
@Contract(pure = true)
public static int compare(@Nullable CharSequence s1, @Nullable CharSequence s2, boolean ignoreCase) {
if (s1 == s2) return 0;
if (s1 == null) return -1;
if (s2 == null) return 1;
int length1 = s1.length();
int length2 = s2.length();
int i = 0;
int j = 0;
for (; i < length1 && j < length2; i++, j++) {
int diff = compare(s1.charAt(i), s2.charAt(j), ignoreCase);
if (diff != 0) {
return diff;
}
}
if (i < length1) return +1;
if (j < length2) return -1;
if (length1 != length2) return length1 - length2;
return ignoreCase ? compare(s1, s2, false) : 0;
}
@Contract(pure = true)
public static int comparePairs(@Nullable String s1, @Nullable String t1, @Nullable String s2, @Nullable String t2, boolean ignoreCase) {
final int compare = compare(s1, s2, ignoreCase);
@@ -3399,4 +3422,4 @@ public class StringUtil extends StringUtilRt {
}
return true;
}
}
}
@@ -19,23 +19,34 @@ import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
/**
* Holds <a href="http://semver.org">Semantic Version</a>.
*/
public final class SemVer implements Comparable<SemVer> {
/** @deprecated */
/**
* @deprecated
*/
@Deprecated public static final SemVer UNKNOWN = new SemVer("?", 0, 0, 0);
private final String myRawVersion;
private final int myMajor;
private final int myMinor;
private final int myPatch;
@Nullable
private final String myPreRelease;
public SemVer(@NotNull String rawVersion, int major, int minor, int patch) {
this(rawVersion, major, minor, patch, null);
}
public SemVer(@NotNull String rawVersion, int major, int minor, int patch, @Nullable String preRelease) {
myRawVersion = rawVersion;
myMajor = major;
myMinor = minor;
myPatch = patch;
myPreRelease = preRelease;
}
@NotNull
@@ -55,9 +66,14 @@ public final class SemVer implements Comparable<SemVer> {
return myPatch;
}
@Nullable
public String getPreRelease() {
return myPreRelease;
}
@NotNull
public String getParsedVersion() {
return myMajor + "." + myMinor + "." + myPatch;
return myMajor + "." + myMinor + "." + myPatch + (myPreRelease != null ? "-" + myPreRelease : "");
}
@Override
@@ -68,7 +84,10 @@ public final class SemVer implements Comparable<SemVer> {
diff = myMinor - other.myMinor;
if (diff != 0) return diff;
return myPatch - other.myPatch;
diff = myPatch - other.myPatch;
if (diff != 0) return diff;
return comparePrerelease(myPreRelease, other.myPreRelease);
}
public boolean isGreaterOrEqualThan(int major, int minor, int patch) {
@@ -83,7 +102,10 @@ public final class SemVer implements Comparable<SemVer> {
if (o == null || getClass() != o.getClass()) return false;
SemVer semVer = (SemVer)o;
return myMajor == semVer.myMajor && myMinor == semVer.myMinor && myPatch == semVer.myPatch;
return myMajor == semVer.myMajor
&& myMinor == semVer.myMinor
&& myPatch == semVer.myPatch
&& Objects.equals(myPreRelease, semVer.myPreRelease);
}
@Override
@@ -91,6 +113,9 @@ public final class SemVer implements Comparable<SemVer> {
int result = myMajor;
result = 31 * result + myMinor;
result = 31 * result + myPatch;
if (myPreRelease != null) {
result = 31 * result + myPreRelease.hashCode();
}
return result;
}
@@ -99,6 +124,83 @@ public final class SemVer implements Comparable<SemVer> {
return myRawVersion;
}
private static int comparePrerelease(@Nullable String pre1, @Nullable String pre2) {
if (pre1 == null) {
return pre2 == null ? 0 : 1;
}
else if (pre2 == null) {
return -1;
}
int length1 = pre1.length();
int length2 = pre2.length();
if (length1 == length2 && pre1.equals(pre2)) return 0;
int start1 = 0;
int start2 = 0;
int diff;
// compare each segment separately
do {
int end1 = pre1.indexOf('.', start1);
int end2 = pre2.indexOf('.', start2);
if (end1 < 0) end1 = length1;
if (end2 < 0) end2 = length2;
CharSequence segment1 = new CharSequenceSubSequence(pre1, start1, end1);
CharSequence segment2 = new CharSequenceSubSequence(pre2, start2, end2);
if (isNumeric(segment1)) {
if (!isNumeric(segment2)) {
return -1;
}
diff = compareNumeric(segment1, segment2);
}
else if (isNumeric(segment2)) {
return 1;
}
else {
diff = StringUtil.compare(segment1, segment2, false);
}
start1 = end1 + 1;
start2 = end2 + 1;
}
while (diff == 0 && start1 < length1 && start2 < length2);
if (diff != 0) return diff;
if (start1 >= length1) {
if (start2 >= length2) {
return 0;
}
return -1;
}
else {
return 1;
}
}
private static int compareNumeric(CharSequence segment1, CharSequence segment2) {
int length1 = segment1.length();
int length2 = segment2.length();
int diff = Integer.compare(length1, length2);
for (int i = 0; i <= length1 && diff == 0; i++) {
diff = segment1.charAt(i) - segment2.charAt(i);
}
return diff;
}
private static boolean isNumeric(CharSequence segment) {
int length = segment.length();
for (int i = 0; i < length; i++) {
int ch = segment.charAt(i);
if (ch < '0' || ch > '9') {
return false;
}
}
return true;
}
@Nullable
public static SemVer parseFromText(@Nullable String text) {
if (text != null) {
@@ -106,14 +208,16 @@ public final class SemVer implements Comparable<SemVer> {
if (majorEndIdx >= 0) {
int minorEndIdx = text.indexOf('.', majorEndIdx + 1);
if (minorEndIdx >= 0) {
int patchEndIdx = text.indexOf('-', minorEndIdx + 1);
if (patchEndIdx < 0) patchEndIdx = text.length();
int preReleaseIdx = text.indexOf('-', minorEndIdx + 1);
int patchEndIdx = preReleaseIdx >= 0 ? preReleaseIdx : text.length();
int major = StringUtil.parseInt(text.substring(0, majorEndIdx), -1);
int minor = StringUtil.parseInt(text.substring(majorEndIdx + 1, minorEndIdx), -1);
int patch = StringUtil.parseInt(text.substring(minorEndIdx + 1, patchEndIdx), -1);
String preRelease = preReleaseIdx >= 0 ? text.substring(preReleaseIdx + 1) : null;
if (major >= 0 && minor >= 0 && patch >= 0) {
return new SemVer(text, major, minor, patch);
return new SemVer(text, major, minor, patch, preRelease);
}
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.util.text;
import org.jetbrains.annotations.Nullable;
import org.junit.Assert;
import org.junit.Test;
@@ -23,15 +24,15 @@ import static org.assertj.core.api.Assertions.assertThat;
public class SemVerTest {
@Test
public void parsing() {
assertParsed("0.9.2", 0, 9, 2);
assertParsed("0.9.2-", 0, 9, 2);
assertParsed("0.9.2-dart", 0, 9, 2);
assertParsed("4.0.0-alpha.1", 4, 0, 0);
assertParsed("0.10.0-rc-1", 0, 10, 0);
assertParsed("1.0.0-rc-1", 1, 0, 0);
assertParsed("1.0.0-alpha", 1, 0, 0);
assertParsed("1.0.0-0.3.7", 1, 0, 0);
assertParsed("1.0.0-x.7.z.92", 1, 0, 0);
assertParsed("0.9.2", 0, 9, 2, null);
assertParsed("0.9.2-", 0, 9, 2, "");
assertParsed("0.9.2-dart", 0, 9, 2, "dart");
assertParsed("4.0.0-alpha.1", 4, 0, 0, "alpha.1");
assertParsed("0.10.0-rc-1", 0, 10, 0, "rc-1");
assertParsed("1.0.0-rc-1", 1, 0, 0, "rc-1");
assertParsed("1.0.0-alpha", 1, 0, 0, "alpha");
assertParsed("1.0.0-0.3.7", 1, 0, 0, "0.3.7");
assertParsed("1.0.0-x.7.z.92", 1, 0, 0, "x.7.z.92");
assertNotParsed(null);
assertNotParsed("");
@@ -42,31 +43,60 @@ public class SemVerTest {
@Test
public void comparing() {
assertThat(parse("1.0.0")).isGreaterThan(parse("0.10.0"));
assertThat(parse("1.0.0")).isLessThan(parse("2.10.0"));
assertThat(parse("0.30.0")).isGreaterThan(parse("0.5.1000"));
assertThat(parse("0.30.10")).isLessThan(parse("0.100.0"));
assertThat(parse("2.9.123-test")).isGreaterThan(parse("2.9.100"));
assertThat(parse("2.9.123-test")).isLessThan(parse("2.9.124"));
assertThat(parse("11.123.0")).isEqualTo(parse("11.123.0"));
assertThat(parse("11.123.0")).isEqualByComparingTo(parse("11.123.0"));
assertThat(parse("11.123.0-a.b.c-1")).isEqualTo(parse("11.123.0-a.b.c-1"));
assertThat(parse("11.123.0-a.b.c-1")).isEqualByComparingTo(parse("11.123.0-a.b.c-1"));
assertPrecedence("0.10.0", "1.0.0");
assertPrecedence("1.0.0","2.10.0");
assertPrecedence("0.5.1000", "0.30.0");
assertPrecedence("0.30.10","0.100.0");
assertPrecedence("2.9.100", "2.9.123-test");
assertPrecedence("2.9.123-test","2.9.124");
assertPrecedence("2.9.123","2.9.124-test");
assertPrecedence("1.2.3-a","1.2.3");
assertPrecedence("1.2.3-12","1.2.3-a");
assertPrecedence("1.2.3-22","1.2.3-100");
assertPrecedence("1.2.3-22","1.2.3-31");
assertPrecedence("1.2.3-a.b.c","1.2.3-a.b.d");
assertPrecedence("1.2.3-a.b.c","1.2.3-a.b.c.a");
assertPrecedence("1.2.3-a.b.1","1.2.3-a.b.c");
assertPrecedence("1.2.3-a.b.1","1.2.3-a.c.1");
assertPrecedence("1.2.3-a.cbc.100","1.2.3-a.cca.1");
assertPrecedence("1.2.3-a.cb.1","1.2.3-a.cba.1");
Assert.assertTrue(parse("4.12.5").isGreaterOrEqualThan(4, 12, 5));
Assert.assertTrue(parse("4.12.5-a").isGreaterOrEqualThan(4, 12, 5));
Assert.assertTrue(parse("4.12.5").isGreaterOrEqualThan(4, 12, 4));
Assert.assertTrue(parse("4.12.5-a").isGreaterOrEqualThan(4, 12, 4));
Assert.assertTrue(parse("4.12.5").isGreaterOrEqualThan(4, 11, 0));
Assert.assertTrue(parse("4.12.5").isGreaterOrEqualThan(4, 11, 9));
Assert.assertTrue(parse("4.12.5").isGreaterOrEqualThan(3, 100, 100));
Assert.assertFalse(parse("4.12.5").isGreaterOrEqualThan(4, 12, 6));
Assert.assertFalse(parse("4.12.5-a").isGreaterOrEqualThan(4, 12, 6));
Assert.assertFalse(parse("4.12.5").isGreaterOrEqualThan(4, 13, 0));
Assert.assertFalse(parse("4.12.5").isGreaterOrEqualThan(5, 1, 0));
}
private static void assertParsed(String version, int expectedMajor, int expectedMinor, int expectedPatch) {
assertThat(parse(version)).isEqualTo(new SemVer(version, expectedMajor, expectedMinor, expectedPatch));
private static void assertPrecedence(String lesserVersion, String higherVersion) {
SemVer v1 = parse(lesserVersion);
SemVer v2 = parse(higherVersion);
assertThat(v1).isLessThan(v2);
assertThat(v2).isGreaterThan(v1);
assertThat(v1).isNotEqualByComparingTo(v2);
assertThat(v1).isNotEqualTo(v2);
}
private static void assertParsed(String version, int expectedMajor, int expectedMinor, int expectedPatch, @Nullable String expectedPreRelease) {
assertThat(parse(version)).isEqualTo(new SemVer(version, expectedMajor, expectedMinor, expectedPatch, expectedPreRelease));
}
private static void assertNotParsed(String version) {
@@ -78,4 +108,4 @@ public class SemVerTest {
assertThat(semVer).describedAs(text).isNotNull();
return semVer;
}
}
}