Added version utility class to manage comparing versions. X.X
.X-BETA-hash is the max supported
This commit is contained in:
parent
4f20a7524b
commit
bb15ca12a8
411
src/dorkbox/util/Version.java
Normal file
411
src/dorkbox/util/Version.java
Normal file
@ -0,0 +1,411 @@
|
||||
/*
|
||||
* Copyright 2017 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dorkbox.util;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Version a = new Version("1.1");
|
||||
* Version b = new Version("1.1.1");
|
||||
* a.compareTo(b) // return -1 (a<b)
|
||||
* a.equals(b) // return false
|
||||
* <p>
|
||||
* Version a = new Version("2.0");
|
||||
* Version b = new Version("1.9.9");
|
||||
* a.compareTo(b) // return 1 (a>b)
|
||||
* a.equals(b) // return false
|
||||
* <p>
|
||||
* Version a = new Version("1.0");
|
||||
* Version b = new Version("1");
|
||||
* a.compareTo(b) // return 0 (a=b)
|
||||
* a.equals(b) // return true
|
||||
* <p>
|
||||
* Version a = new Version("1");
|
||||
* Version b = null;
|
||||
* a.compareTo(b) // return 1 (a>b)
|
||||
* a.equals(b) // return false
|
||||
* <p>
|
||||
* List<Version> versions = new ArrayList<Version>();
|
||||
* versions.add(new Version("2"));
|
||||
* versions.add(new Version("1.0.5"));
|
||||
* versions.add(new Version("1.01.0"));
|
||||
* versions.add(new Version("1.00.1"));
|
||||
* Collections.min(versions).get() // return min version
|
||||
* Collections.max(versions).get() // return max version
|
||||
* <p>
|
||||
* // WARNING
|
||||
* Version a = new Version("2.06");
|
||||
* Version b = new Version("2.060");
|
||||
* a.equals(b) // return false
|
||||
* <p>
|
||||
* <p>
|
||||
* If the numbers are the same, then
|
||||
* BETA+BUILD < BETA < STABLE+BUILD < STABLE.
|
||||
* <p>
|
||||
* Stable is a version that is exclusively numbers. Builds are always equal, even if a different build commit hash/etc.
|
||||
*/
|
||||
@SuppressWarnings({"unused", "SimplifiableIfStatement"})
|
||||
public
|
||||
class Version implements Comparable<Version> {
|
||||
private static final int[] PRIME = {2, 3, 5};
|
||||
|
||||
private final String version;
|
||||
private final int[] internalVersion;
|
||||
|
||||
private final boolean isBeta;
|
||||
private final String build;
|
||||
|
||||
/**
|
||||
* Creates a comparable version based on only numbers
|
||||
*
|
||||
* @param version must consist of just numbers with a maximum of 3 groups separated by a .
|
||||
*/
|
||||
public
|
||||
Version(String version) {
|
||||
this(version, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a comparable version based on numbers, BETA status, and BUILD
|
||||
*
|
||||
* @param version must consist of only numbers with a maximum of 3 groups separated by a .
|
||||
* @param isBeta true if this is a beta build
|
||||
* @param build custom build info, such as the commit sha hash
|
||||
*/
|
||||
public
|
||||
Version(String version, boolean isBeta, String build) {
|
||||
if (version == null) {
|
||||
throw new IllegalArgumentException("Version can not be null");
|
||||
}
|
||||
if (!version.matches("[0-9]+(\\.[0-9]+){0,3}")) {
|
||||
throw new IllegalArgumentException("Invalid version format");
|
||||
}
|
||||
|
||||
if (build != null) {
|
||||
this.build = build.toLowerCase(Locale.US);
|
||||
}
|
||||
else {
|
||||
this.build = null;
|
||||
}
|
||||
|
||||
this.isBeta = isBeta;
|
||||
this.version = version;
|
||||
|
||||
String[] parts = this.version.split("\\.");
|
||||
internalVersion = new int[parts.length];
|
||||
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
final String s = parts[i];
|
||||
internalVersion[i] = Integer.parseInt(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a comparable version based on only numbers
|
||||
*/
|
||||
public
|
||||
Version(String... version) {
|
||||
if (version == null) {
|
||||
throw new IllegalArgumentException("Version can not be null");
|
||||
}
|
||||
|
||||
int length = version.length;
|
||||
if (length > 3) {
|
||||
throw new IllegalArgumentException("Invalid version format");
|
||||
}
|
||||
|
||||
this.build = null;
|
||||
this.isBeta = false;
|
||||
|
||||
StringBuilder builder = new StringBuilder(length + 3);
|
||||
internalVersion = new int[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
final String s = version[i];
|
||||
if (!MathUtil.isInteger(s)) {
|
||||
throw new IllegalArgumentException("Version must be a number");
|
||||
}
|
||||
internalVersion[i] = Integer.parseInt(s);
|
||||
builder.append(s)
|
||||
.append('.');
|
||||
}
|
||||
|
||||
this.version = builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a comparable version based on only numbers
|
||||
*/
|
||||
public
|
||||
Version(int... version) {
|
||||
if (version == null) {
|
||||
throw new IllegalArgumentException("Version can not be null");
|
||||
}
|
||||
|
||||
if (version.length > 3) {
|
||||
throw new IllegalArgumentException("Invalid version format");
|
||||
}
|
||||
|
||||
this.build = null;
|
||||
this.isBeta = false;
|
||||
|
||||
StringBuilder builder = new StringBuilder(version.length + 3);
|
||||
internalVersion = new int[version.length];
|
||||
for (int i = 0; i < version.length; i++) {
|
||||
internalVersion[i] = version[i];
|
||||
builder.append(i)
|
||||
.append('.');
|
||||
}
|
||||
|
||||
this.version = builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a version into a "beta" version, without any additional build information
|
||||
* <p>
|
||||
* BETA+BUILD < BETA < STABLE+BUILD < STABLE.
|
||||
* Stable is a version that is exclusively numbers. Builds are always equal, even if a different build commit hash/etc.
|
||||
*/
|
||||
public
|
||||
Version beta() {
|
||||
return new Version(version, true, build);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a version with specific build information (such as sha commit hash, etc)
|
||||
* <p>
|
||||
* BETA+BUILD < BETA < STABLE+BUILD < STABLE.
|
||||
* Stable is a version that is exclusively numbers. Builds are always equal, even if a different build commit hash/etc.
|
||||
*/
|
||||
public
|
||||
Version build(String build) {
|
||||
return new Version(version, isBeta, build);
|
||||
}
|
||||
|
||||
public
|
||||
boolean isGreater(Object that) {
|
||||
if (this == that) {
|
||||
return false;
|
||||
}
|
||||
if (that == null) {
|
||||
return true;
|
||||
}
|
||||
if (this.getClass() != that.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.compareTo((Version) that) > 0;
|
||||
}
|
||||
|
||||
public
|
||||
boolean isGreaterOrEquals(Object that) {
|
||||
if (this == that) {
|
||||
return true;
|
||||
}
|
||||
if (that == null) {
|
||||
return true;
|
||||
}
|
||||
if (this.getClass() != that.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.compareTo((Version) that) >= 0;
|
||||
}
|
||||
|
||||
public
|
||||
boolean isLess(Object that) {
|
||||
if (this == that) {
|
||||
return false;
|
||||
}
|
||||
if (that == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.getClass() != that.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.compareTo((Version) that) < 0;
|
||||
}
|
||||
|
||||
public
|
||||
boolean isLessOrEquals(Object that) {
|
||||
if (this == that) {
|
||||
return true;
|
||||
}
|
||||
if (that == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.getClass() != that.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.compareTo((Version) that) <= 0;
|
||||
}
|
||||
|
||||
public
|
||||
boolean isEquals(Object that) {
|
||||
return equals(that);
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
boolean equals(Object that) {
|
||||
if (this == that) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (that == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.getClass() != that.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.compareTo((Version) that) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
int compareTo(Version that) {
|
||||
if (that == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int[] thisParts = this.internalVersion;
|
||||
int[] thatParts = that.internalVersion;
|
||||
|
||||
int maxLength = Math.max(thisParts.length, thatParts.length);
|
||||
|
||||
for (int i = 0; i < maxLength; i++) {
|
||||
int thisPart;
|
||||
if (i < thisParts.length) {
|
||||
thisPart = thisParts[i];
|
||||
}
|
||||
else {
|
||||
thisPart = 0;
|
||||
}
|
||||
|
||||
int thatPart;
|
||||
if (i < thatParts.length) {
|
||||
thatPart = thatParts[i];
|
||||
}
|
||||
else {
|
||||
thatPart = 0;
|
||||
}
|
||||
|
||||
if (thisPart < thatPart) {
|
||||
return -1;
|
||||
}
|
||||
if (thisPart > thatPart) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// our numbers are all equal, now determine equality based on BETA/BUILD info
|
||||
|
||||
// BETA+BUILD < BETA < STABLE+BUILD < STABLE.
|
||||
// Stable is a version that is exclusively numbers. Builds are always equal, even if a different build commit hash/etc.
|
||||
|
||||
if (this.isBeta) {
|
||||
if (this.build != null) {
|
||||
if (that.isBeta) {
|
||||
if (that.build != null) {
|
||||
// BETA+BUILD == BETA+BUILD
|
||||
return 0;
|
||||
}
|
||||
// BETA+BUILD < BETA
|
||||
return -1;
|
||||
}
|
||||
// BETA+BUILD < STABLE+BUILD < STABLE
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
if (that.isBeta) {
|
||||
if (that.build != null) {
|
||||
// BETA > BETA+BUILD
|
||||
return 1;
|
||||
}
|
||||
// BETA == BETA
|
||||
return 0;
|
||||
}
|
||||
// BETA < STABLE+BUILD < STABLE
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// else this is STABLE or STABLE+BUILD
|
||||
|
||||
if (this.build != null) {
|
||||
if (that.isBeta) {
|
||||
// STABLE+BUILD > BETA > BETA+BUILD
|
||||
return 1;
|
||||
}
|
||||
if (that.build != null) {
|
||||
// STABLE+BUILD == STABLE+BUILD
|
||||
return 0;
|
||||
}
|
||||
// STABLE+BUILD < STABLE
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
if (that.isBeta) {
|
||||
// STABLE > BETA > BETA+BUILD
|
||||
return 1;
|
||||
}
|
||||
if (that.build != null) {
|
||||
// STABLE > STABLE+BUILD
|
||||
return 1;
|
||||
}
|
||||
// STABLE == STABLE
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final
|
||||
int hashCode() {
|
||||
// better hashing than just using .toString().hashCode()
|
||||
int hashCode = 0;
|
||||
for (int i = 0; i < internalVersion.length; i++) {
|
||||
final int part = internalVersion[i];
|
||||
if (part > 0) {
|
||||
hashCode += PRIME[i] ^ part;
|
||||
}
|
||||
}
|
||||
|
||||
if (build != null) {
|
||||
hashCode += build.hashCode();
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public
|
||||
String toString() {
|
||||
if (isBeta) {
|
||||
if (build != null) {
|
||||
return version + "-BETA+" + build;
|
||||
}
|
||||
return version + "-BETA";
|
||||
}
|
||||
if (build != null) {
|
||||
return version + "+" + build;
|
||||
}
|
||||
return version;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user