diff --git a/src/main/java/com/github/zafarkhaja/semver/MetadataVersion.java b/src/main/java/com/github/zafarkhaja/semver/MetadataVersion.java index 83b46ab..ce615c9 100644 --- a/src/main/java/com/github/zafarkhaja/semver/MetadataVersion.java +++ b/src/main/java/com/github/zafarkhaja/semver/MetadataVersion.java @@ -31,6 +31,47 @@ import java.util.Arrays; */ class MetadataVersion implements Comparable { + static final MetadataVersion NULL = new NullMetadataVersion(); + + private static class NullMetadataVersion extends MetadataVersion { + + public NullMetadataVersion() { + super(null); + } + + @Override + MetadataVersion increment() { + throw new NullPointerException("Metadata version is NULL"); + } + + @Override + public String toString() { + return ""; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object other) { + return other instanceof NullMetadataVersion; + } + + @Override + public int compareTo(MetadataVersion other) { + if (!equals(other)) { + /** + * Pre-release versions have a lower precedence than + * the associated normal version. (SemVer p.9) + */ + return 1; + } + return 0; + } + } + private final String[] idents; MetadataVersion(String[] identifiers) { @@ -69,14 +110,21 @@ class MetadataVersion implements Comparable { @Override public String toString() { StringBuilder sb = new StringBuilder(); - for (String id : idents) { - sb.append(id).append("."); + for (String ident : idents) { + sb.append(ident).append("."); } return sb.deleteCharAt(sb.lastIndexOf(".")).toString(); } @Override public int compareTo(MetadataVersion other) { + if (other == MetadataVersion.NULL) { + /** + * Pre-release versions have a lower precedence than + * the associated normal version. (SemVer p.9) + */ + return -1; + } int result = compareIdentifierArrays(other.idents); if (result == 0) { result = idents.length - other.idents.length; @@ -100,11 +148,11 @@ class MetadataVersion implements Comparable { return arr1.length <= arr2.length ? arr1.length : arr2.length; } - private int compareIdentifiers(String id1, String id2) { - if (isInt(id1) && isInt(id2)) { - return Integer.parseInt(id1) - Integer.parseInt(id2); + private int compareIdentifiers(String ident1, String ident2) { + if (isInt(ident1) && isInt(ident2)) { + return Integer.parseInt(ident1) - Integer.parseInt(ident2); } else { - return id1.compareTo(id2); + return ident1.compareTo(ident2); } } diff --git a/src/main/java/com/github/zafarkhaja/semver/Version.java b/src/main/java/com/github/zafarkhaja/semver/Version.java index 2970399..40e1839 100644 --- a/src/main/java/com/github/zafarkhaja/semver/Version.java +++ b/src/main/java/com/github/zafarkhaja/semver/Version.java @@ -44,36 +44,28 @@ public class Version implements Comparable { private String preRelease; private String build; - public Builder(String normalVersion) { - if (normalVersion == null) { + public Builder(String normal) { + if (normal == null) { throw new NullPointerException( "Normal version MUST NOT be NULL" ); } - normal = normalVersion; + this.normal = normal; } - public void setPreReleaseVersion(String preReleaseVersion) { - preRelease = preReleaseVersion; + public void setPreReleaseVersion(String preRelease) { + this.preRelease = preRelease; } - public void setBuildMetadata(String buildMetadata) { - build = buildMetadata; + public void setBuildMetadata(String build) { + this.build = build; } public Version build() { - MetadataVersion preReleaseVersion = null; - if (preRelease != null) { - preReleaseVersion = VersionParser.parsePreRelease(preRelease); - } - MetadataVersion buildMetadata = null; - if (build != null) { - buildMetadata = VersionParser.parseBuild(build); - } return new Version( VersionParser.parseVersionCore(normal), - preReleaseVersion, - buildMetadata + VersionParser.parsePreRelease(preRelease), + VersionParser.parseBuild(build) ); } } @@ -86,32 +78,28 @@ public class Version implements Comparable { public int compare(Version v1, Version v2) { int result = v1.compareTo(v2); if (result == 0) { - result = compareBuilds(v1, v2); - } - return result; - } - - private int compareBuilds(Version v1, Version v2) { - int result = 0; - if (v1.build != null && v2.build != null) { result = v1.build.compareTo(v2.build); - } else if (v1.build == null ^ v2.build == null) { - /** - * Build versions should have a higher precedence - * than the associated normal version. - */ - result = (v1.build == null) ? -1 : 1; + if (v1.build == MetadataVersion.NULL || + v2.build == MetadataVersion.NULL + ) { + /** + * Build metadata should have a higher precedence + * than the associated normal version which is the + * opposite compared to pre-release versions. + */ + result = -1 * result; + } } return result; } } Version(NormalVersion normal) { - this(normal, null, null); + this(normal, MetadataVersion.NULL, MetadataVersion.NULL); } Version(NormalVersion normal, MetadataVersion preRelease) { - this(normal, preRelease, null); + this(normal, preRelease, MetadataVersion.NULL); } Version( @@ -162,16 +150,10 @@ public class Version implements Comparable { } public Version incrementPreReleaseVersion() { - if (preRelease == null) { - throw new NullPointerException("Pre-release version is NULL"); - } return new Version(normal, preRelease.increment()); } public Version incrementBuildMetadata() { - if (build == null) { - throw new NullPointerException("Build metadata is NULL"); - } return new Version(normal, preRelease, build.increment()); } @@ -207,11 +189,11 @@ public class Version implements Comparable { } public String getPreReleaseVersion() { - return (preRelease != null) ? preRelease.toString() : ""; + return preRelease.toString(); } public String getBuildMetadata() { - return (build != null) ? build.toString() : ""; + return build.toString(); } public boolean greaterThan(Version other) { @@ -244,19 +226,19 @@ public class Version implements Comparable { @Override public int hashCode() { int hash = 5; - hash = 97 * hash + (normal != null ? normal.hashCode() : 0); - hash = 97 * hash + (preRelease != null ? preRelease.hashCode() : 0); - hash = 97 * hash + (build != null ? build.hashCode() : 0); + hash = 97 * hash + normal.hashCode(); + hash = 97 * hash + preRelease.hashCode(); + hash = 97 * hash + build.hashCode(); return hash; } @Override public String toString() { StringBuilder sb = new StringBuilder(getNormalVersion()); - if (preRelease != null) { + if (!getPreReleaseVersion().isEmpty()) { sb.append(PRE_RELEASE_PREFIX).append(getPreReleaseVersion()); } - if (build != null) { + if (!getBuildMetadata().isEmpty()) { sb.append(BUILD_PREFIX).append(getBuildMetadata()); } return sb.toString(); @@ -266,7 +248,7 @@ public class Version implements Comparable { public int compareTo(Version other) { int result = normal.compareTo(other.normal); if (result == 0) { - result = comparePreReleases(other); + result = preRelease.compareTo(other.preRelease); } return result; } @@ -274,18 +256,4 @@ public class Version implements Comparable { public int compareWithBuildsTo(Version other) { return BUILD_AWARE_ORDER.compare(this, other); } - - private int comparePreReleases(Version other) { - int result = 0; - if (preRelease != null && other.preRelease != null) { - result = preRelease.compareTo(other.preRelease); - } else if (preRelease == null ^ other.preRelease == null) { - /** - * Pre-release versions have a lower precedence than - * the associated normal version. (SemVer p.9) - */ - result = (preRelease == null) ? 1 : -1; - } - return result; - } } diff --git a/src/main/java/com/github/zafarkhaja/semver/VersionParser.java b/src/main/java/com/github/zafarkhaja/semver/VersionParser.java index 516cee4..ef28f61 100644 --- a/src/main/java/com/github/zafarkhaja/semver/VersionParser.java +++ b/src/main/java/com/github/zafarkhaja/semver/VersionParser.java @@ -173,19 +173,25 @@ class VersionParser implements Parser { } static MetadataVersion parsePreRelease(String preRelease) { + if (preRelease == null) { + return MetadataVersion.NULL; + } VersionParser parser = new VersionParser(preRelease); return parser.parsePreRelease(); } static MetadataVersion parseBuild(String build) { + if (build == null) { + return MetadataVersion.NULL; + } VersionParser parser = new VersionParser(build); return parser.parseBuild(); } private Version parseValidSemVer() { NormalVersion normalVersion = parseVersionCore(); - MetadataVersion preReleaseVersion = null; - MetadataVersion buildMetadata = null; + MetadataVersion preReleaseVersion = MetadataVersion.NULL; + MetadataVersion buildMetadata = MetadataVersion.NULL; if (chars.positiveLookahead(HYPHEN)) { chars.consume(); preReleaseVersion = parsePreRelease(); diff --git a/src/test/java/com/github/zafarkhaja/semver/MetadataVersionTest.java b/src/test/java/com/github/zafarkhaja/semver/MetadataVersionTest.java index a679f76..4f5c994 100644 --- a/src/test/java/com/github/zafarkhaja/semver/MetadataVersionTest.java +++ b/src/test/java/com/github/zafarkhaja/semver/MetadataVersionTest.java @@ -81,6 +81,13 @@ public class MetadataVersionTest { assertTrue(0 < v1.compareTo(v2)); } + @Test + public void shouldReturnNegativeWhenComparedToNullMetadataVersion() { + MetadataVersion v1 = new MetadataVersion(new String[] {}); + MetadataVersion v2 = MetadataVersion.NULL; + assertTrue(0 > v1.compareTo(v2)); + } + @Test public void shouldOverrideEqualsMethod() { MetadataVersion v1 = new MetadataVersion( @@ -124,6 +131,55 @@ public class MetadataVersionTest { } } + public static class NullMetadataVersionTest { + + @Test + public void shouldReturnEmptyStringOnToString() { + MetadataVersion v = MetadataVersion.NULL; + assertTrue(v.toString().isEmpty()); + } + + @Test + public void shouldReturnZeroOnHashCode() { + MetadataVersion v = MetadataVersion.NULL; + assertEquals(0, v.hashCode()); + } + + @Test + public void shouldBeEqualOnlyToItsType() { + MetadataVersion v1 = MetadataVersion.NULL; + MetadataVersion v2 = MetadataVersion.NULL; + MetadataVersion v3 = new MetadataVersion(new String[] {}); + assertTrue(v1.equals(v2)); + assertTrue(v2.equals(v1)); + assertFalse(v1.equals(v3)); + } + + @Test + public void shouldReturnPositiveWhenComparedToNonNullMetadataVersion() { + MetadataVersion v1 = MetadataVersion.NULL; + MetadataVersion v2 = new MetadataVersion(new String[] {}); + assertTrue(0 < v1.compareTo(v2)); + } + + @Test + public void shouldReturnZeroWhenComparedToNullMetadataVersion() { + MetadataVersion v1 = MetadataVersion.NULL; + MetadataVersion v2 = MetadataVersion.NULL; + assertTrue(0 == v1.compareTo(v2)); + } + + @Test + public void shouldThrowNullPointerExceptionIfIncremented() { + try { + MetadataVersion.NULL.increment(); + } catch (NullPointerException e) { + return; + } + fail("Should throw NullPointerException when incremented"); + } + } + public static class EqualsMethodTest { @Test diff --git a/src/test/java/com/github/zafarkhaja/semver/VersionParserTest.java b/src/test/java/com/github/zafarkhaja/semver/VersionParserTest.java index 6831c7a..ac2b895 100644 --- a/src/test/java/com/github/zafarkhaja/semver/VersionParserTest.java +++ b/src/test/java/com/github/zafarkhaja/semver/VersionParserTest.java @@ -54,6 +54,12 @@ public class VersionParserTest { assertEquals(new MetadataVersion(new String[] {"beta-1", "1"}), preRelease); } + @Test + public void shouldReturnNullMetadataVersionIfPreReleaseIsNull() { + MetadataVersion preRelease = VersionParser.parsePreRelease(null); + assertEquals(MetadataVersion.NULL, preRelease); + } + @Test public void shouldNotAllowDigitsInPreReleaseVersion() { try { @@ -80,6 +86,12 @@ public class VersionParserTest { assertEquals(new MetadataVersion(new String[] {"build", "1"}), build); } + @Test + public void shouldReturnNullMetadataVersionIfBuildIsNull() { + MetadataVersion build = VersionParser.parseBuild(null); + assertEquals(MetadataVersion.NULL, build); + } + @Test public void shouldAllowDigitsInBuildMetadata() { try {