I have an application that communicates with the firmware of a device. As there are changes to the firmware, it is versioned with the format {major}.{minor}.{revision}
For my project I have used this approach by following Semantic Versioning 2.0.0:
private static String[] formatVersionString(String[] strArr){
//remove trailing 0s
List<String> list = new ArrayList<>();
boolean foundChar = false;
for(int i=strArr.length-1;i>=0;i--){
String curChar = strArr[i];
if(curChar.equals("0") && !foundChar){
continue;
} else{
list.add(strArr[i]);
foundChar = true;
}
}
Collections.reverse(list);
return list.toArray(new String[list.size()]);
}
private static String getPreReleaseBuildStr(String buildStr){
//removing build metadata
if(buildStr == null){
return null;
}
String [] a = buildStr.split("\\+");
if(a.length>0){
return a[0];
} else{
return null;
}
}
private static int compareVersionString(String str1,String str2){
int ret = 0;
String[] verStr1 = formatVersionString(str1.split("\\."));
String[] verStr2 = formatVersionString(str2.split("\\."));
int i = 0;
// set index to first non-equal ordinal or length of shortest version string
while (i < verStr1.length && i < verStr2.length && verStr1[i].equals(verStr2[i])) {
i++;
}
// compare first non-equal ordinal number
if (i < verStr1.length && i < verStr2.length) {
int diff = 0;
try{
if(verStr1[i] == null || verStr1[i].trim().length() == 0) {
verStr1[i] = "0";
}
if(verStr2[i] == null || verStr2[i].trim().length() == 0) {
verStr2[i] = "0";
}
diff = Integer.valueOf(verStr1[i]).compareTo(Integer.valueOf(verStr2[i]));
}catch(NumberFormatException e){
diff = verStr1[i].compareTo(verStr2[i]);
} finally{
ret = Integer.signum(diff);
}
} else{
// the strings are equal or one string is a substring of the other
// e.g. "1.2.3" = "1.2.3" or "1.2.3" < "1.2.3.4"
ret = Integer.signum(verStr1.length - verStr2.length);
}
return ret;
}
/**
* Compares two version strings.
* follow this link for more info http://semver.org/
*
* Use this instead of String.compareTo() for a non-lexicographical
* comparison that works for version strings. e.g. "1.10".compareTo("1.6").
*
* Ex:--
* //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 < 2.0.0.6
*
* @param str1 a string of ordinal numbers separated by decimal points.
* @param str2 a string of ordinal numbers separated by decimal points.
* @return The result is a negative integer if str1 is _numerically_ less than str2.
* The result is a positive integer if str1 is _numerically_ greater than str2.
* The result is zero if the strings are _numerically_ equal.
*/
public static int versionCompare(String str1, String str2) {
int ret = 0;
String[] val1 = str1.split("-");
String[] val2 = str2.split("-");
String preReleaseVer1 = null, preReleaseVer2 = null;
if(val1.length>1){
preReleaseVer1 = getPreReleaseBuildStr(val1[1]);
}
if(val2.length>1){
preReleaseVer2 = getPreReleaseBuildStr(val2[1]);
}
ret = compareVersionString(val1[0],val2[0]);
if(ret == 0){
//if both version are equal then compare with pre_release String
if(preReleaseVer1 == null && preReleaseVer2 == null){
ret = 0;
} else if(preReleaseVer1 == null && preReleaseVer2!=null){
//1.0.0 > 1.0.0-beta
ret = 1;
} else if(preReleaseVer1 != null && preReleaseVer2==null){
//1.0.0-beta < 1.0.0
ret = -1;
} else{
//both hasve pre release string
ret = compareVersionString(preReleaseVer1,preReleaseVer2);
}
}
return ret;
}
Use versionCompare(String str1, String str2) method for any comparison
Code Example:---
public class Main
{
public static void main (String[] args)
{
//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
Test(new String[]{"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","2.0.0.1"});
}
private static void Test(String[] versions) {
for (int i = 0; i < versions.length; i++) {
for (int j = 0; j < versions.length; j++) {
Test(versions[i], versions[j]);
}
}
}
private static void Test(String v1, String v2) {
int result = versionCompare(v1,v2);
String op = "==";
if (result < 0) op = "<";
if (result > 0) op = ">";
System.out.printf("%s %s %s\n", v1, op, v2);
}
}
The Output of the above example is given below:---
1.0.0-alpha == 1.0.0-alpha
1.0.0-alpha < 1.0.0-alpha.1
1.0.0-alpha < 1.0.0-alpha.beta
1.0.0-alpha < 1.0.0-beta
1.0.0-alpha < 1.0.0-beta.2
1.0.0-alpha < 1.0.0-beta.11
1.0.0-alpha < 1.0.0-rc.1
1.0.0-alpha < 1.0.0
1.0.0-alpha < 2.0.0.1
1.0.0-alpha.1 > 1.0.0-alpha
1.0.0-alpha.1 == 1.0.0-alpha.1
1.0.0-alpha.1 < 1.0.0-alpha.beta
1.0.0-alpha.1 < 1.0.0-beta
1.0.0-alpha.1 < 1.0.0-beta.2
1.0.0-alpha.1 < 1.0.0-beta.11
1.0.0-alpha.1 < 1.0.0-rc.1
1.0.0-alpha.1 < 1.0.0
1.0.0-alpha.1 < 2.0.0.1
1.0.0-alpha.beta > 1.0.0-alpha
1.0.0-alpha.beta > 1.0.0-alpha.1
1.0.0-alpha.beta == 1.0.0-alpha.beta
1.0.0-alpha.beta < 1.0.0-beta
1.0.0-alpha.beta < 1.0.0-beta.2
1.0.0-alpha.beta < 1.0.0-beta.11
1.0.0-alpha.beta < 1.0.0-rc.1
1.0.0-alpha.beta < 1.0.0
1.0.0-alpha.beta < 2.0.0.1
1.0.0-beta > 1.0.0-alpha
1.0.0-beta > 1.0.0-alpha.1
1.0.0-beta > 1.0.0-alpha.beta
1.0.0-beta == 1.0.0-beta
1.0.0-beta < 1.0.0-beta.2
1.0.0-beta < 1.0.0-beta.11
1.0.0-beta < 1.0.0-rc.1
1.0.0-beta < 1.0.0
1.0.0-beta < 2.0.0.1
1.0.0-beta.2 > 1.0.0-alpha
1.0.0-beta.2 > 1.0.0-alpha.1
1.0.0-beta.2 > 1.0.0-alpha.beta
1.0.0-beta.2 > 1.0.0-beta
1.0.0-beta.2 == 1.0.0-beta.2
1.0.0-beta.2 < 1.0.0-beta.11
1.0.0-beta.2 < 1.0.0-rc.1
1.0.0-beta.2 < 1.0.0
1.0.0-beta.2 < 2.0.0.1
1.0.0-beta.11 > 1.0.0-alpha
1.0.0-beta.11 > 1.0.0-alpha.1
1.0.0-beta.11 > 1.0.0-alpha.beta
1.0.0-beta.11 > 1.0.0-beta
1.0.0-beta.11 > 1.0.0-beta.2
1.0.0-beta.11 == 1.0.0-beta.11
1.0.0-beta.11 < 1.0.0-rc.1
1.0.0-beta.11 < 1.0.0
1.0.0-beta.11 < 2.0.0.1
1.0.0-rc.1 > 1.0.0-alpha
1.0.0-rc.1 > 1.0.0-alpha.1
1.0.0-rc.1 > 1.0.0-alpha.beta
1.0.0-rc.1 > 1.0.0-beta
1.0.0-rc.1 > 1.0.0-beta.2
1.0.0-rc.1 > 1.0.0-beta.11
1.0.0-rc.1 == 1.0.0-rc.1
1.0.0-rc.1 < 1.0.0
1.0.0-rc.1 < 2.0.0.1
1.0.0 > 1.0.0-alpha
1.0.0 > 1.0.0-alpha.1
1.0.0 > 1.0.0-alpha.beta
1.0.0 > 1.0.0-beta
1.0.0 > 1.0.0-beta.2
1.0.0 > 1.0.0-beta.11
1.0.0 > 1.0.0-rc.1
1.0.0 == 1.0.0
1.0.0 < 2.0.0.1
2.0.0.1 > 1.0.0-alpha
2.0.0.1 > 1.0.0-alpha.1
2.0.0.1 > 1.0.0-alpha.beta
2.0.0.1 > 1.0.0-beta
2.0.0.1 > 1.0.0-beta.2
2.0.0.1 > 1.0.0-beta.11
2.0.0.1 > 1.0.0-rc.1
2.0.0.1 > 1.0.0
2.0.0.1 == 2.0.0.1
3.5.6-beta == 3.5.6-beta
3.5.6-beta == 3.5.6-beta.0
3.5.6-beta.0 == 3.5.6-beta
3.5.6-beta.0 == 3.5.6-beta.0
I would implement a comparable class:
class Version implements Comparable<Version> {
int major;
int minor;
int rev;
int beta = Integer.MAX_VALUE;
public int compareTo(Version o) {
if (this.major != o.major) {
return Integer.compare(this.major, o.major);
}
if (this.minor != o.minor) {
return Integer.compare(this.minor, o.minor);
}
if (this.rev != o.rev) {
return Integer.compare(this.rev, o.rev);
}
if (this.beta != o.beta) {
return Integer.compare(this.beta, o.beta);
}
return 0;
}
public static Version parse(String version) {
// TODO: implement parsing here
// 1.1.1 - beta = MAX_VALUE
// 1.1.1beta - beta = 1
// 1.1.1beta2 - beta = 2
return new Version();
}
@Override
public String toString() {
return "" + major + "." + minor + "." + rev
+ (beta == Integer.MAX_VALUE ? ""
: (beta == 1 ? "beta" :
("beta" + beta)));
}
}
Then compare in a standard java way:
if (Version.parse(testFW).compareTo(Version.parse(baseFW)) < 0) {
// Version is newer!
}
Here's one suggestion:
static int[] getVersionNumbers(String ver) {
Matcher m = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)(beta(\\d*))?")
.matcher(ver);
if (!m.matches())
throw new IllegalArgumentException("Malformed FW version");
return new int[] { Integer.parseInt(m.group(1)), // major
Integer.parseInt(m.group(2)), // minor
Integer.parseInt(m.group(3)), // rev.
m.group(4) == null ? Integer.MAX_VALUE // no beta suffix
: m.group(5).isEmpty() ? 1 // "beta"
: Integer.parseInt(m.group(5)) // "beta3"
};
}
static boolean isFirmwareNewer(String testFW, String baseFW) {
int[] testVer = getVersionNumbers(testFW);
int[] baseVer = getVersionNumbers(baseFW);
for (int i = 0; i < testVer.length; i++)
if (testVer[i] != baseVer[i])
return testVer[i] > baseVer[i];
return true;
}
It uses a little trick and translates the beta-part as follows:
""
(no beta suffix) → Beta MAX_INT"beta"
→ Beta 1 (since it preceeds "beta2")"betaX"
→ Beta XNote that it return true
if both versions are identical.