package org.cloudsmith.geppetto.semver;
import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A class that implements a <a href="http://semver.org/spec/v1.0.0.html">Semantic Versioning 1.0.0</a>.
*/
public class Version implements Comparable<Version>, Serializable {
private static final long serialVersionUID = 1L;
public static final String SNAPSHOT_SUFFIX = "-SNAPSHOT";
public static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9a-zA-Z-]*))?$");
public static final Pattern PRE_RELEASE_PATTERN = Pattern.compile("^[0-9a-zA-Z-]*$");
public static final Version MAX = new Version(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, null);
public static final String MIN_PRE_RELEASE = "";
public static final Version MIN = new Version(0, 0, 0, MIN_PRE_RELEASE);
public static int comparePreReleases(String p1, String p2) {
int cmp = 0;
if(p1 == null) {
if(p2 != null)
cmp = 1;
}
else if(p2 == null)
cmp = -1;
else
cmp = p1.compareTo(p2);
return cmp;
}
public static Version create(int major, int minor, int patch) {
return create(major, minor, patch, null);
}
public static Version create(int major, int minor, int patch, String preRelease) {
if(major < 0 || minor < 0 || patch < 0)
throw new IllegalArgumentException("Negative numbers not accepted in version");
if(preRelease != null && preRelease.length() >= 0 && !PRE_RELEASE_PATTERN.matcher(preRelease).matches())
throw new IllegalArgumentException("Illegal characters in pre-release");
return new Version(major, minor, patch, preRelease);
}
public static Version create(String version) {
if(version == null || version.length() == 0)
return null;
Matcher m = VERSION_PATTERN.matcher(version);
if(!m.matches())
throw new IllegalArgumentException("The string '" + version +
"' does not represent a valid semantic version");
return new Version(
Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)), Integer.parseInt(m.group(3)), m.group(4));
}
private final int major;
private final int minor;
private final int patch;
private final String preRelease;
private Version(int major, int minor, int patch, String preRelease) {
this.major = major;
this.minor = minor;
this.patch = patch;
this.preRelease = preRelease;
}
@Override
public int compareTo(Version o) {
int cmp = major - o.major;
if(cmp == 0) {
cmp = minor - o.minor;
if(cmp == 0) {
cmp = patch - o.patch;
if(cmp == 0)
cmp = comparePreReleases(preRelease, o.preRelease);
}
}
return cmp;
}
@Override
public boolean equals(Object o) {
if(o instanceof Version) {
Version v = (Version) o;
return major == v.major && minor == v.minor && patch == v.patch &&
(preRelease == v.preRelease || preRelease != null && preRelease.equals(v.preRelease));
}
return false;
}
public int getMajor() {
return major;
}
public int getMinor() {
return minor;
}
public int getPatch() {
return patch;
}
public String getPreRelease() {
return preRelease;
}
@Override
public int hashCode() {
int hash = major;
hash = 31 * hash + minor;
hash = 31 * hash + patch;
if(preRelease != null)
hash = 31 * hash + preRelease.hashCode();
return hash;
}
public boolean isSnapshot() {
return preRelease != null && preRelease.endsWith(SNAPSHOT_SUFFIX);
}
public boolean isStable() {
return major > 0 && !isSnapshot();
}
@Override
public String toString() {
StringBuilder bld = new StringBuilder();
toString(bld);
return bld.toString();
}
public void toString(StringBuilder bld) {
bld.append(major);
bld.append('.');
bld.append(minor);
bld.append('.');
bld.append(patch);
if(preRelease != null) {
bld.append('-');
bld.append(preRelease);
}
}
}