@@ -28,6 +28,15 @@ public Version(@NotNull String prerelease) {
2828
2929 }
3030
31+ private static boolean isDigitString (String str ) {
32+ for (char c : str .toCharArray ()) {
33+ if (!Character .isDigit (c )) {
34+ return false ;
35+ }
36+ }
37+ return true ;
38+ }
39+
3140 public String asVersionString () {
3241 var string = new StringBuilder ();
3342 string
@@ -47,8 +56,8 @@ public String asVersionString() {
4756 }
4857
4958 /**
50- * Compares this version to the given version.
51- * Versions that are the same return false.
59+ * Compares this version to the given version.
60+ * Versions that are the same return false.
5261 *
5362 * @param version the version to compare to
5463 * @return true if this version is newer than the given version
@@ -72,6 +81,16 @@ public boolean isStable() {
7281 return this .prerelease == null ;
7382 }
7483
84+ /**
85+ * Compares this version to the given version.
86+ * Versions that are exactly the same return true.
87+ * Versions that are equal for the purposes of semver may return false.
88+ * <p>
89+ * This method takes into account build Metadata.
90+ *
91+ * @param o the object to compare to
92+ * @return true if the object is a version and is equal to this version
93+ */
7594 @ Override
7695 public boolean equals (Object o ) {
7796 if (this == o ) return true ;
@@ -98,9 +117,11 @@ public String toString() {
98117 }
99118
100119 /**
101- * Calcualtes the difference between two versions.
102- *
120+ * Calculates the difference between two versions.
121+ * <p>
103122 * PRERELEASE versions are compared lexicographically.
123+ * Per semver, a prerelease version is always less than a stable version of the same major.minor.patch
124+ *
104125 * <p>
105126 * Compares this object with the specified object for order. Returns a
106127 * negative integer, zero, or a positive integer as this object is less
@@ -120,7 +141,7 @@ public String toString() {
120141 * x.compareTo(y)==0} implies that {@code signum(x.compareTo(z))
121142 * == signum(y.compareTo(z))}, for all {@code z}.
122143 *
123- * @param o the object to be compared.
144+ * @param version the object to be compared.
124145 * @return a negative integer, zero, or a positive integer as this object
125146 * is less than, equal to, or greater than the specified object.
126147 * @throws NullPointerException if the specified object is null
@@ -135,6 +156,36 @@ public String toString() {
135156 */
136157 @ Override
137158 public int compareTo (@ NotNull Version version ) {
159+ /*
160+ https://semver.org/#spec-item-11
161+
162+ Precedence refers to how versions are compared to each other when ordered.
163+
164+ Precedence MUST be calculated by separating the version into major, minor, patch and pre-release identifiers
165+ in that order (Build metadata does not figure into precedence).
166+
167+ Precedence is determined by the first difference when comparing each of these identifiers from left to right
168+ as follows: Major, minor, and patch versions are always compared numerically.
169+
170+ Example: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1.
171+
172+ When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version:
173+
174+ Example: 1.0.0-alpha < 1.0.0.
175+
176+ Precedence for two pre-release versions with the same major, minor, and patch version MUST be determined by
177+ comparing each dot separated identifier from left to right until a difference is found as follows:
178+
179+ Identifiers consisting of only digits are compared numerically.
180+
181+ Identifiers with letters or hyphens are compared lexically in ASCII sort order.
182+
183+ Numeric identifiers always have lower precedence than non-numeric identifiers.
184+
185+ A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal.
186+
187+ Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.
188+ */
138189
139190 if (this .equals (version ))
140191 return 0 ;
@@ -160,14 +211,54 @@ else if (this.patch < version.patch) {
160211 // A prerelease version is always less than a stable version of the same major.minor.patch
161212 if (this .isStable () && !version .isStable ()) {
162213 return 1 ;
163- } else if (!this .isStable ()&& version .isStable ()) {
214+ } else if (!this .isStable () && version .isStable ()) {
164215 return -1 ;
165216 }
166217
167-
168218 if (this .prerelease != null && version .prerelease != null ) {
219+ /*
220+ Precedence for two pre-release versions with the same major, minor, and patch version MUST be determined
221+ by comparing each dot separated identifier from left to right until a difference is found as follows:
222+
223+ Identifiers consisting of only digits are compared numerically.
224+
225+ Identifiers with letters or hyphens are compared lexically in ASCII sort order.
226+
227+ Numeric identifiers always have lower precedence than non-numeric identifiers.
228+
229+ A larger set of pre-release fields has a higher precedence than a smaller set,
230+ if all of the preceding identifiers are equal.
231+ */
232+
233+ // separate prerelease identifiers by dot from left to right
234+ var thisPrereleaseIdentifiers = this .prerelease .split ("\\ ." );
235+ var versionPrereleaseIdentifiers = version .prerelease .split ("\\ ." );
236+ for (int i = 0 ; i < thisPrereleaseIdentifiers .length ; i ++) {
237+ if (i >= versionPrereleaseIdentifiers .length ) {
238+ // this prerelease version has more identifiers than the other prerelease version
239+ return 1 ;
240+ }
241+
242+ // Identifiers consisting of only digits are compared numerically.
243+ if (isDigitString (thisPrereleaseIdentifiers [i ]) && isDigitString (versionPrereleaseIdentifiers [i ])) {
244+ var result = Integer .compare (
245+ Integer .parseInt (thisPrereleaseIdentifiers [i ]),
246+ Integer .parseInt (versionPrereleaseIdentifiers [i ]));
247+ if (result != 0 ) {
248+ return result ;
249+ }
250+ }
251+
252+ // Identifiers with letters or hyphens are compared lexically in ASCII sort order.
253+ var result = thisPrereleaseIdentifiers [i ].compareTo (versionPrereleaseIdentifiers [i ]);
254+ if (result != 0 ) {
255+ return result ;
256+ }
257+ }
258+
259+
169260 // return reverse comparison because prerelease versions are compared lexicographically
170- return -version .prerelease .compareTo (this .prerelease );
261+ // return -version.prerelease.compareTo(this.prerelease);
171262 }
172263
173264 return 0 ;
0 commit comments