pax_global_header00006660000000000000000000000064144517765010014524gustar00rootroot0000000000000052 comment=26c8e653eeb2e2c637a3ef8d174fea25cba49670 libsemantic-version-java-2.1.1+ds/000077500000000000000000000000001445177650100170235ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/.gitignore000066400000000000000000000003441445177650100210140ustar00rootroot00000000000000*.class # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.ear # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* .classpath .project .settings/ target/libsemantic-version-java-2.1.1+ds/Jenkinsfile000066400000000000000000000016031445177650100212070ustar00rootroot00000000000000pipeline { agent { docker { image 'maven:3.6-jdk-11' args '-v /home/jenkins/.m2:/var/maven/.m2 -v /home/jenkins/.gnupg:/.gnupg -e MAVEN_CONFIG=/var/maven/.m2 -e MAVEN_OPTS=-Duser.home=/var/maven' } } environment { COVERALLS_REPO_TOKEN = credentials('coveralls_repo_token_semantic_version') GPG_SECRET = credentials('gpg_password') } stages { stage('Build') { steps { sh 'mvn -B clean verify' } } stage('Coverage') { steps { sh 'mvn -B jacoco:report jacoco:report-integration coveralls:report -DrepoToken=$COVERALLS_REPO_TOKEN' } } stage('javadoc') { steps { sh 'mvn -B javadoc:javadoc' } } stage('Deploy SNAPSHOT') { when { branch 'dev' } steps { sh 'mvn -B -Prelease -DskipTests -Dgpg.passphrase=${GPG_SECRET} deploy' } } } } libsemantic-version-java-2.1.1+ds/JenkinsfileRelease000066400000000000000000000034061445177650100225130ustar00rootroot00000000000000pipeline { agent { docker { image 'maven:3.6-jdk-11' args '-v /home/jenkins/.m2:/var/maven/.m2 -v /home/jenkins/.gnupg:/.gnupg -e MAVEN_CONFIG=/var/maven/.m2 -e MAVEN_OPTS=-Duser.home=/var/maven' } } environment { GPG_SECRET = credentials('gpg_password') GITHUB = credentials('Github-Username-Pw') GITHUB_RELEASE_TOKEN = credentials('github_registry_release') GIT_ASKPASS='./.git-askpass' } stages { stage ('Ensure dev branch') { when { expression { return env.BRANCH_NAME != 'dev'; } } steps { error("Releasing is only possible from dev branch") } } stage ('Set Git Information') { steps { sh 'echo \'echo \$GITHUB_PSW\' > ./.git-askpass' sh 'chmod +x ./.git-askpass' sh 'git config url."https://api@github.com/".insteadOf "https://github.com/"' sh 'git config url."https://ssh@github.com/".insteadOf "ssh://git@github.com/"' sh 'git config url."https://git@github.com/".insteadOf "git@github.com:"' sh 'git config user.email "build@taddiken.online"' sh 'git config user.name "Jenkins"' } } stage('Create release branch') { steps { sh 'mvn -B -Prelease gitflow:release-start' } } stage('Verify release') { steps { sh 'mvn -B -Prelease -Dgpg.passphrase=${GPG_SECRET} verify' } } stage('Perform release') { steps { sh "mvn -B gitflow:release-finish -DargLine=\"-Prelease -B -Dgpg.passphrase=${GPG_SECRET} -DskipTests\"" } } stage('Create GitHub release') { steps { sh 'git checkout master' sh "mvn -B github-release:github-release -Dgithub.release-token=${GITHUB_RELEASE_TOKEN}" } } } } libsemantic-version-java-2.1.1+ds/LICENSE000066400000000000000000000020701445177650100200270ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Simon Taddiken Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.libsemantic-version-java-2.1.1+ds/README.md000066400000000000000000000131251445177650100203040ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/skuzzle/semantic-version.svg?branch=master)](https://travis-ci.org/skuzzle/semantic-version) [![Coverage Status](https://coveralls.io/repos/github/skuzzle/semantic-version/badge.svg?branch=master)](https://coveralls.io/github/skuzzle/semantic-version?branch=master) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/de.skuzzle/semantic-version/badge.svg)](https://maven-badges.herokuapp.com/maven-central/de.skuzzle/semantic-version) [![JavaDoc](http://javadoc-badge.appspot.com/de.skuzzle/semantic-version.svg?label=JavaDoc)](http://javadoc-badge.appspot.com/de.skuzzle/semantic-version) [![Twitter Follow](https://img.shields.io/twitter/follow/skuzzleOSS.svg?style=social)](https://twitter.com/skuzzleOSS) semantic-version ================ This is a single-class [semantic version 2.0.0](http://semver.org/) implementation for java 6+. It requires no further dependencies and is thereby easy to use within your own projects. Key features: * Lightweight: consists of only a single file, no dependencies * Immutable: strict immutability ensures easy handling and thread safety * Serializable: Objects can be serialized using Java's `ObjectOutputStream`. * Fast: Many performance improvements make this the fastest semver implementation in java around (according to parsing and sorting performance) * Compatible: Supports Java 6 but also provides many methods that are suitable to be used as method references in Java 8. Latest release also features a Java 9 module-info! * Stable: Ready for production since release 1.0.0 ## Maven Dependency semantic-version is available through the Maven Central Repository. Just add the following dependency to your pom: ```xml de.skuzzle semantic-version 2.1.0 ``` ## Java 9 Releases `>=2.0.0` are bundled as a JPMS module. If you are using it in your Java 9 project, add the following line to your `module-info.java`: ``` module com.your.module { // ... requires de.skuzzle.semantic; } ``` ## Usage ### Creation and parsing ```java // Version with pre-release and build meta data field Version v1 = Version.parseVersion("1.0.2-rc1.2+build-20142402"); Version v2 = Version.create(1, 0 , 2, "rc1.2", "build-20142402"); // Simple version Version v3 = Version.parseVersion("1.0.2"); Version v4 = Version.create(1, 0, 2); // Version with no pre-release field but with build meta data field Version v5 = Version.parseVersion("1.0.2+build-20142402"); Version v6 = Version.create(1, 0, 2, "", "build-20142402"); ``` ### Comparing Versions can be compared as they implement `Comparable`: ```java if (v1.compareTo(v2) < 0) { ... } if (v1.isGreaterThan(v2)) { ... } if (v1.isLowerThan(v2)) { ... } ``` In rare cases it might be useful to compare versions with including the build meta data field. If you need to do so, you can use ```java v1.compareToWithBuildMetaData(v2) v1.equalsWithBuildMetaData(v2) ``` There also exist static methods and comparators for comparing two versions. ### Deriving You can derive new versions from existing ones by modifying a single field: ```java Version v1 = Version.create(1, 0, 0) .withMinor(2) .withPatch(3) .withPreRelease("alpha-1") .withBuildMetaData("build-20161022"); ``` ### Incrementing Versions can also be incremented using any of the `next...` methods: ``` // Gives 2.0.0 Version.create(1, 2, 3).nextMajor(); // Gives 1.3.0 Version.create(1, 2, 3).nextMinor(); // Gives 1.2.4 Version.create(1, 2, 3).nextPatch(); ``` All `next...` methods will drop the pre-release and build meta data fields but provide an overload to set a new pre-release: ``` // Gives 2.0.0-SNAPSHOT Version.create(1, 2, 3).nextMajor("SNAPSHOT"); ``` The identifier parts can be incremented as well: ``` // Gives 1.2.3-1 Version.create(1, 2, 3).nextPreRelease(); // Gives 1.2.3+1 Version.create(1, 2, 3).nextBuildMetaData(); ``` Incrementing the identifier behaves as follows: * In case the identifier is currently empty, it becomes `1` in the result. * If the identifier's last part is numeric, that last part will be incremented in the result. * If the last part is not numeric, the identifier is interpreted as `identifier.0` which becomes `identifier.1` after increment. Version | After increment --------| --------------- `1.2.3`| `1.2.3-1` `1.2.3+build.meta.data` | `1.2.3-1` `1.2.3-foo` | `1.2.3-foo.1` `1.2.3-foo.1` | `1.2.3-foo.2` The special method `toStable` which has been introduced in version 2.1.0 will give give the next _stable_ version. That is, it simply drops the pre-release and build meta data identifiers and leaves all other parts unmodified. ### Serialization Versions can be written to/read from streams by Java's `ObjectOutputStream` and `ObjectInputStream` classes out of the box: ```java new ObjectOutputStream(yourOutStream).writeObject(Version.parseVersion("1.2.3")); Version version = (Version) new ObjectInputStream(yourInStream).readObject(); ``` Serializing Versions from and to json is also possible but requires third party libraries like `jackson` or `gson`. Support for those is not built in (in order to not ship extra dependencies) but examples can be found within the unit tests [here (jackson)](https://github.com/skuzzle/semantic-version/blob/master/src/test/java/de/skuzzle/semantic/CustomJacksonSerialization.java) and [here (gson)](https://github.com/skuzzle/semantic-version/blob/master/src/test/java/de/skuzzle/semantic/CustomGsonSerialization.java). Both examples will serialize the Version as its String representation as opposed to destructing it into its single fields. libsemantic-version-java-2.1.1+ds/RELEASE_NOTES.md000066400000000000000000000001741445177650100213770ustar00rootroot00000000000000* #5: System locale might lead to illegal identifiers during lower/upper casing (Thx [@portlek](https://github.com/portlek))libsemantic-version-java-2.1.1+ds/pom.xml000066400000000000000000000153171445177650100203470ustar00rootroot00000000000000 4.0.0 de.skuzzle skuzzle-parent 3.0.1 semantic-version 2.1.1 jar semantic-version Single-class semantic version implementation for java The MIT License (MIT) http://opensource.org/licenses/MIT repo semantic-version semantic-version 5.7.2 2.10.0.pr1 2.8.5 0.9.0 3.1.0 false false true scm:git:https://github.com/skuzzle/${github.name}.git HEAD org.junit junit-bom ${junit.version} pom import com.google.code.gson gson ${gson.version} test com.fasterxml.jackson.core jackson-databind ${jackson.version} test com.fasterxml.jackson.core jackson-core ${jackson.version} test com.github.zafarkhaja java-semver ${java-semver.version} test com.vdurmont semver4j ${semver4j.version} test org.junit.jupiter junit-jupiter test org.junit.jupiter junit-jupiter-api test org.junit-pioneer junit-pioneer 1.4.2 test org.apache.maven.plugins maven-javadoc-plugin 8 com.amashchenko.maven.plugin gitflow-maven-plugin deploy true true true master dev com.ragedunicorn.tools.maven github-release-maven-plugin 1.0.2 skuzzle ${github.name} ${github.release-token} v${project.version} Semantic Version ${project.version} master RELEASE_NOTES.md org.apache.maven.plugins maven-compiler-plugin default-compile 9 default-testCompile testCompile 9 base-compile compile module-info.java 9 6 libsemantic-version-java-2.1.1+ds/src/000077500000000000000000000000001445177650100176125ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/it/000077500000000000000000000000001445177650100202265ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/it/java/000077500000000000000000000000001445177650100211475ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/it/java/de/000077500000000000000000000000001445177650100215375ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/it/java/de/skuzzle/000077500000000000000000000000001445177650100232465ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/it/java/de/skuzzle/semantic/000077500000000000000000000000001445177650100250515ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/it/java/de/skuzzle/semantic/IsPreReleasePerformanceIT.java000066400000000000000000000015031445177650100326550ustar00rootroot00000000000000package de.skuzzle.semantic; import org.junit.jupiter.api.Test; public class IsPreReleasePerformanceIT extends VersionPerformanceTestBase { private static final String INVALID_PRE_RELEASE = "very.long-prelease.id.1234.01"; @Test public void testIsNoPreReleaseWithRegex() throws Exception { performTest("Is no pre-release with regex", RUNS, new Runnable() { @Override public void run() { VersionRegEx.isValidPreRelease(INVALID_PRE_RELEASE); } }); } @Test public void testIsNoPreRelease() throws Exception { performTest("Is no pre-release without regex", RUNS, new Runnable() { @Override public void run() { Version.isValidPreRelease(INVALID_PRE_RELEASE); } }); } } libsemantic-version-java-2.1.1+ds/src/it/java/de/skuzzle/semantic/ParsingPerformanceIT.java000066400000000000000000000016441445177650100317430ustar00rootroot00000000000000package de.skuzzle.semantic; import org.junit.jupiter.api.Test; import com.vdurmont.semver4j.Semver; public class ParsingPerformanceIT extends VersionPerformanceTestBase { private static final String TEST_STRING = "10.153.132-123a.sdfasd.asd.asdhd.124545f.very.long-prelease.012aid.1234+with.build.md.000112"; @Test public void testNoRegex() throws Exception { performTest("Without regex", RUNS, () -> Version.parseVersion(TEST_STRING)); } @Test public void testWithRegex() throws Exception { performTest("With regex", RUNS, () -> VersionRegEx.parseVersion(TEST_STRING)); } @Test public void testJSemver() throws Exception { performTest("jsemver", RUNS, () -> com.github.zafarkhaja.semver.Version.valueOf(TEST_STRING)); } @Test public void testsemver4j() throws Exception { performTest("semver4j", RUNS, () -> new Semver(TEST_STRING)); } } libsemantic-version-java-2.1.1+ds/src/it/java/de/skuzzle/semantic/SortingPerformanceIT.java000066400000000000000000000051721445177650100317650ustar00rootroot00000000000000package de.skuzzle.semantic; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Random; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import com.vdurmont.semver4j.Semver; public class SortingPerformanceIT extends VersionPerformanceTestBase { private static final Random SHUFFLE_RANDOM = new Random(0); private List sortMe; @BeforeEach public void setup() { final List mutable = new ArrayList<>( List.of( "1.0.0-alpha.beta", "1.0.0-alpha", "1.0.0-alpha.1", "1.0.0-beta.11", "1.0.0-beta", "1.0.0-rc.1", "1.0.0", "1.0.0-beta.2", "2.1.1", "2.1.0", "1.0.0-alpha.beta", "2.0.0", "1.0.0-alpha", "1.0.0-rc.1", "1.0.0-alpha.1", "1.0.0-beta", "1.0.0-beta.11", "1.0.0", "1.0.0-beta.2", "2.1.0", "2.0.0", "2.1.1")); Collections.shuffle(mutable, SHUFFLE_RANDOM); sortMe = Collections.unmodifiableList(mutable); } @Test public void testOldVersion() throws Exception { performTest("Regex Impl", RUNS, () -> sortMe.stream() .map(VersionRegEx::parseVersion) .toArray(VersionRegEx[]::new), Arrays::sort); } @Test public void testCurrentVersion() throws Exception { performTest("Current Impl", RUNS, () -> sortMe.stream() .map(Version::parseVersion) .toArray(Version[]::new), Arrays::sort); } @Test public void testJSemver() throws Exception { performTest("JSemver", RUNS, () -> sortMe.stream() .map(com.github.zafarkhaja.semver.Version::valueOf) .toArray(com.github.zafarkhaja.semver.Version[]::new), Arrays::sort); } @Test public void testSemver4j() throws Exception { performTest("JSemver", RUNS, () -> sortMe.stream() .map(Semver::new) .toArray(Semver[]::new), Arrays::sort); } } libsemantic-version-java-2.1.1+ds/src/it/java/de/skuzzle/semantic/VersionPerformanceTestBase.java000066400000000000000000000051641445177650100331640ustar00rootroot00000000000000package de.skuzzle.semantic; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Consumer; import java.util.function.Supplier; class VersionPerformanceTestBase { private static final int WARM_UP = 11000; protected static final int RUNS = 100000; private void warmUp(Runnable subject) { for (int i = 0; i < WARM_UP; ++i) { subject.run(); } } private void warmUp(Supplier before, Consumer subject) { for (int i = 0; i < WARM_UP; ++i) { subject.accept(before.get()); } } protected void performTest(String description, int iterations, Supplier beforeEach, Consumer subject) { System.out.println("Test: " + description); warmUp(beforeEach, subject); long min = Long.MAX_VALUE; long max = 0; long sum = 0; final List times = new ArrayList<>(iterations); for (int i = 0; i < iterations; ++i) { final T t = beforeEach.get(); final long start = System.nanoTime(); subject.accept(t); final long time = System.nanoTime() - start; times.add(time); min = Math.min(min, time); max = Math.max(max, time); sum += time; } Collections.sort(times); final long avg = sum / iterations; final long median = times.get(times.size() / 2); System.out.println("Min " + min); System.out.println("Max " + max); System.out.println("Avg " + avg); System.out.println("Med " + median); System.out.println(); } protected void performTest(String description, int iterations, Runnable subject) { System.out.println("Test: " + description); warmUp(subject); long min = Long.MAX_VALUE; long max = 0; long sum = 0; final List times = new ArrayList<>(iterations); for (int i = 0; i < iterations; ++i) { final long start = System.nanoTime(); subject.run(); final long time = System.nanoTime() - start; times.add(time); min = Math.min(min, time); max = Math.max(max, time); sum += time; } Collections.sort(times); final long avg = sum / iterations; final long median = times.get(times.size() / 2); System.out.println("Min " + min); System.out.println("Max " + max); System.out.println("Avg " + avg); System.out.println("Med " + median); System.out.println(); } }libsemantic-version-java-2.1.1+ds/src/it/java/de/skuzzle/semantic/VersionRegEx.java000066400000000000000000000760671445177650100303140ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2015 Simon Taddiken * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package de.skuzzle.semantic; import java.io.Serializable; import java.util.Comparator; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This class is an implementation of the full semantic version 2.0.0 * specification. Instances can be obtained using the * static overloads of the create method or by {@link #parseVersion(String) * parsing} a String. This class implements {@link Comparable} to compare two versions by * following the specifications linked to above. The {@link #equals(Object)} method * conforms to the result of {@link #compareTo(VersionRegEx)}, {@link #hashCode()} is * implemented appropriately. Neither method considers the {@link #getBuildMetaData() * build meta data} field for comparison. * *

* Instances of this class are fully immutable. *

* *

* Note that unless stated otherwise, none of the public methods of this class accept * null values. Most methods will throw an {@link IllegalArgumentException} * when encountering a null argument. However, to comply with the * {@link Comparable} contract, the comparison methods will throw a * {@link NullPointerException} instead. *

* * @author Simon Taddiken */ public final class VersionRegEx implements Comparable, Serializable { /** Conforms to all Version implementations since 0.6.0 */ private static final long serialVersionUID = 6034927062401119911L; private static final String[] EMPTY_ARRAY = new String[0]; /** * Semantic Version Specification to which this class complies * * @since 0.2.0 */ public static final VersionRegEx COMPLIANCE = VersionRegEx.create(2, 0, 0); /** * This exception indicates that a version- or a part of a version string could not be * parsed according to the semantic version specification. * * @author Simon Taddiken */ public static class VersionFormatException extends RuntimeException { private static final long serialVersionUID = 1L; /** * Creates a new VersionFormatException with the given message. * * @param message The exception message. */ public VersionFormatException(String message) { super(message); } } /** * Comparator for natural version ordering. See {@link #compare(VersionRegEx, VersionRegEx)} for * more information. * * @since 0.2.0 */ public static final Comparator NATURAL_ORDER = new Comparator() { @Override public int compare(VersionRegEx o1, VersionRegEx o2) { return VersionRegEx.compare(o1, o2); } }; /** * Comparator for ordering versions with additionally considering the build meta data * field when comparing versions. * *

* Note: this comparator imposes orderings that are inconsistent with equals. *

* * @since 0.3.0 */ public static final Comparator WITH_BUILD_META_DATA_ORDER = new Comparator() { @Override public int compare(VersionRegEx o1, VersionRegEx o2) { return compareWithBuildMetaData(o1, o2); } }; private static final Pattern PRE_RELEASE = Pattern.compile("" + "(?:(?:[0-9]+[a-zA-Z-][\\w-]*)|(?:[a-zA-Z][\\w-]*)|(?:[1-9]\\d*)|0)" + "(?:\\.(?:(?:[0-9]+[a-zA-Z-][\\w-]*)|(?:[a-zA-Z][\\w-]*)|(?:[1-9]\\d*)|0))*"); private static final Pattern BUILD_MD = Pattern.compile("[\\w-]+(\\.[\\w-]+)*"); private static final Pattern VERSION_PATTERN = Pattern.compile("" + "(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)" + "(?:-((?:(?:[0-9]+[a-zA-Z-][\\w-]*)|(?:[a-zA-Z][\\w-]*)|(?:[1-9]\\d*)|0)" + "(?:\\.(?:(?:[0-9]+[a-zA-Z-][\\w-]*)|(?:[a-zA-Z][\\w-]*)|(?:[1-9]\\d*)|0))*))?" + "(?:\\+([\\w-]+(\\.[\\w-]+)*))?"); // Match result group indices private static final int MAJOR_GROUP = 1; private static final int MINOR_GROUP = 2; private static final int PATCH_GROUP = 3; private static final int PRE_RELEASE_GROUP = 4; private static final int BUILD_MD_GROUP = 5; private static final int TO_STRING_ESTIMATE = 12; private static final int HASH_PRIME = 31; private final int major; private final int minor; private final int patch; private final String preRelease; private final String buildMetaData; // store hash code once it has been calculated private volatile int hash; private VersionRegEx(int major, int minor, int patch, String preRelease, String buildMd) { this.major = major; this.minor = minor; this.patch = patch; this.preRelease = preRelease; this.buildMetaData = buildMd; } /** * Tries to parse the given String as a semantic version and returns whether the * String is properly formatted according to the semantic version specification. * *

* Note: this method does not throw an exception upon null input, but * returns false instead. *

* * @param version The String to check. * @return Whether the given String is formatted as a semantic version. * @since 0.5.0 */ public static boolean isValidVersion(String version) { if (version == null || version.isEmpty()) { return false; } return VERSION_PATTERN.matcher(version).matches(); } /** * Returns whether the given String is a valid pre-release identifier. That is, this * method returns true if, and only if the {@code preRelease} parameter * is either the empty string or properly formatted as a pre-release identifier * according to the semantic version specification. * *

* Note: this method does not throw an exception upon null input, but * returns false instead. *

* * @param preRelease The String to check. * @return Whether the given String is a valid pre-release identifier. * @since 0.5.0 */ public static boolean isValidPreRelease(String preRelease) { return preRelease != null && (preRelease.isEmpty() || PRE_RELEASE.matcher(preRelease).matches()); } /** * Returns whether the given String is a valid build meta data identifier. That is, * this method returns true if, and only if the {@code buildMetaData} * parameter is either the empty string or properly formatted as a build meta data * identifier according to the semantic version specification. * *

* Note: this method does not throw an exception upon null input, but * returns false instead. *

* * @param buildMetaData The String to check. * @return Whether the given String is a valid build meta data identifier. * @since 0.5.0 */ public static boolean isValidBuildMetaData(String buildMetaData) { return buildMetaData != null && (buildMetaData.isEmpty() || BUILD_MD.matcher(buildMetaData).matches()); } /** * Returns the greater of the two given versions by comparing them using their natural * ordering. If both versions are equal, then the first argument is returned. * * @param v1 The first version. * @param v2 The second version. * @return The greater version. * @throws IllegalArgumentException If either argument is null. * @since 0.4.0 */ public static VersionRegEx max(VersionRegEx v1, VersionRegEx v2) { require(v1 != null, "v1 is null"); require(v2 != null, "v2 is null"); return compare(v1, v2, false) < 0 ? v2 : v1; } /** * Returns the lower of the two given versions by comparing them using their natural * ordering. If both versions are equal, then the first argument is returned. * * @param v1 The first version. * @param v2 The second version. * @return The lower version. * @throws IllegalArgumentException If either argument is null. * @since 0.4.0 */ public static VersionRegEx min(VersionRegEx v1, VersionRegEx v2) { require(v1 != null, "v1 is null"); require(v2 != null, "v2 is null"); return compare(v1, v2, false) <= 0 ? v1 : v2; } /** * Compares two versions, following the semantic version specification. Here * is a quote from semantic version 2.0.0 * specification: * *

* Precedence refers to how versions are compared to each other when ordered. * Precedence MUST be calculated by separating the version into major, minor, patch * and pre-release identifiers in that order (Build metadata does not figure into * precedence). Precedence is determined by the first difference when comparing each * of these identifiers from left to right as follows: Major, minor, and patch * versions are always compared numerically. Example: 1.0.0 < 2.0.0 < 2.1.0 < * 2.1.1. When major, minor, and patch are equal, a pre-release version has lower * precedence than a normal version. Example: 1.0.0-alpha < 1.0.0. Precedence for * two pre-release versions with the same major, minor, and patch version MUST be * determined by comparing each dot separated identifier from left to right until a * difference is found as follows: identifiers consisting of only digits are compared * numerically and identifiers with letters or hyphens are compared lexically in ASCII * sort order. Numeric identifiers always have lower precedence than non-numeric * identifiers. A larger set of pre-release fields has a higher precedence than a * smaller set, if all of the preceding identifiers are equal. 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. *

* *

* This method fulfills the general contract for Java's {@link Comparator Comparators} * and {@link Comparable Comparables}. *

* * @param v1 The first version for comparison. * @param v2 The second version for comparison. * @return A value below 0 iff {@code v1 < v2}, a value above 0 iff * {@code v1 > v2 and 0 iff v1 = v2}. * @throws NullPointerException If either parameter is null. * @since 0.2.0 */ public static int compare(VersionRegEx v1, VersionRegEx v2) { // throw NPE to comply with Comparable specification if (v1 == null) { throw new NullPointerException("v1 is null"); } else if (v2 == null) { throw new NullPointerException("v2 is null"); } return compare(v1, v2, false); } /** * Compares two Versions with additionally considering the build meta data field if * all other parts are equal. Note: This is not part of the semantic version * specification. * *

* Comparison of the build meta data parts happens exactly as for pre release * identifiers. Considering of build meta data first kicks in if both versions are * equal when using their natural order. *

* *

* This method fulfills the general contract for Java's {@link Comparator Comparators} * and {@link Comparable Comparables}. *

* * @param v1 The first version for comparison. * @param v2 The second version for comparison. * @return A value below 0 iff {@code v1 < v2}, a value above 0 iff * {@code v1 > v2
and 0 iff v1 = v2}. * @throws NullPointerException If either parameter is null. * @since 0.3.0 */ public static int compareWithBuildMetaData(VersionRegEx v1, VersionRegEx v2) { // throw NPE to comply with Comparable specification if (v1 == null) { throw new NullPointerException("v1 is null"); } else if (v2 == null) { throw new NullPointerException("v2 is null"); } return compare(v1, v2, true); } private static int compare(VersionRegEx v1, VersionRegEx v2, boolean withBuildMetaData) { int result = 0; if (v1 != v2) { final int mc, mm, mp, pr, md; if ((mc = compareInt(v1.major, v2.major)) != 0) { result = mc; } else if ((mm = compareInt(v1.minor, v2.minor)) != 0) { result = mm; } else if ((mp = compareInt(v1.patch, v2.patch)) != 0) { result = mp; } else if ((pr = comparePreRelease(v1, v2)) != 0) { result = pr; } else if (withBuildMetaData && ((md = compareBuildMetaData(v1, v2)) != 0)) { result = md; } } return result; } private static int compareInt(int a, int b) { return a - b; } private static int comparePreRelease(VersionRegEx v1, VersionRegEx v2) { return compareLiterals(v1.getPreRelease(), v2.getPreRelease()); } private static int compareBuildMetaData(VersionRegEx v1, VersionRegEx v2) { return compareLiterals(v1.getBuildMetaData(), v2.getBuildMetaData()); } private static int compareLiterals(String v1Literal, String v2Literal) { int result = 0; if (!v1Literal.isEmpty() && !v2Literal.isEmpty()) { // compare pre release parts result = compareIdentifiers(v1Literal.split("\\."), v2Literal.split("\\.")); } else if (!v1Literal.isEmpty()) { // other is greater, because it is no pre release result = -1; } else if (!v2Literal.isEmpty()) { // this is greater because other is no pre release result = 1; } return result; } private static int compareIdentifiers(String[] parts1, String[] parts2) { final int min = Math.min(parts1.length, parts2.length); for (int i = 0; i < min; ++i) { final int r = compareIdentifierParts(parts1[i], parts2[i]); if (r != 0) { // versions differ in part i return r; } } // all id's are equal, so compare amount of id's return compareInt(parts1.length, parts2.length); } private static int compareIdentifierParts(String p1, String p2) { final int num1 = isNumeric(p1); final int num2 = isNumeric(p2); final int result; if (num1 < 0 && num2 < 0) { // both are not numerical -> compare lexically result = p1.compareTo(p2); } else if (num1 >= 0 && num2 >= 0) { // both are numerical result = compareInt(num1, num2); } else if (num1 >= 0) { // only part1 is numerical -> p2 is greater result = -1; } else { // only part2 is numerical -> p1 is greater result = 1; } return result; } /** * Determines whether s is a positive number. If it is, the number is returned, * otherwise the result is -1. * * @param s The String to check. * @return The positive number (incl. 0) if s a number, or -1 if it is not. */ private static int isNumeric(String s) { try { return Integer.parseInt(s); } catch (final NumberFormatException e) { return -1; } } /** * Creates a new Version from the provided components. Neither value of * {@code major, minor} or {@code patch} must be lower than 0 and at least one must be * greater than zero. {@code preRelease} or {@code buildMetaData} may be the empty * String. In this case, the created {@code Version} will have no pre release resp. * build meta data field. If those parameters are not empty, they must conform to the * semantic version specification. * * @param major The major version. * @param minor The minor version. * @param patch The patch version. * @param preRelease The pre release version or the empty string. * @param buildMetaData The build meta data field or the empty string. * @return The version instance. * @throws VersionFormatException If {@code preRelease} or {@code buildMetaData} does * not conform to the semantic version specification. */ public static final VersionRegEx create(int major, int minor, int patch, String preRelease, String buildMetaData) { checkParams(major, minor, patch); require(preRelease != null, "preRelease is null"); require(buildMetaData != null, "buildMetaData is null"); if (!isValidPreRelease(preRelease)) { throw new VersionFormatException( String.format("Illegal pre-release part: %s", preRelease)); } else if (!isValidBuildMetaData(buildMetaData)) { throw new VersionFormatException( String.format("Illegal build-meta-data part: %s", buildMetaData)); } return new VersionRegEx(major, minor, patch, preRelease, buildMetaData); } /** * Creates a new Version from the provided components. The version's build meta data * field will be empty. Neither value of {@code major, minor} or {@code patch} must be * lower than 0 and at least one must be greater than zero. {@code preRelease} may be * the empty String. In this case, the created version will have no pre release field. * If it is not empty, it must conform to the specifications of the semantic version. * * @param major The major version. * @param minor The minor version. * @param patch The patch version. * @param preRelease The pre release version or the empty string. * @return The version instance. * @throws VersionFormatException If {@code preRelease} is not empty and does not * conform to the semantic versioning specification */ public static final VersionRegEx create(int major, int minor, int patch, String preRelease) { checkParams(major, minor, patch); require(preRelease != null, "preRelease is null"); if (!preRelease.isEmpty() && !PRE_RELEASE.matcher(preRelease).matches()) { throw new VersionFormatException(preRelease); } return new VersionRegEx(major, minor, patch, preRelease, ""); } /** * Creates a new Version from the three provided components. The version's pre release * and build meta data fields will be empty. Neither value must be lower than 0 and at * least one must be greater than zero * * @param major The major version. * @param minor The minor version. * @param patch The patch version. * @return The version instance. */ public static final VersionRegEx create(int major, int minor, int patch) { checkParams(major, minor, patch); return new VersionRegEx(major, minor, patch, "", ""); } private static void checkParams(int major, int minor, int patch) { require(major >= 0, "major < 0"); require(minor >= 0, "minor < 0"); require(patch >= 0, "patch < 0"); require(major != 0 || minor != 0 || patch != 0, "all parts are 0"); } private static void require(boolean condition, String message) { if (!condition) { throw new IllegalArgumentException(message); } } /** * Tries to parse the provided String as a semantic version. If the string does not * conform to the semantic version specification, a {@link VersionFormatException} * will be thrown. * * @param versionString The String to parse. * @return The parsed version. * @throws VersionFormatException If the String is no valid version * @throws IllegalArgumentException If {@code versionString} is null. */ public static final VersionRegEx parseVersion(String versionString) { require(versionString != null, "versionString is null"); final Matcher m = VERSION_PATTERN.matcher(versionString); if (!m.matches()) { throw new VersionFormatException(versionString); } final int major = Integer.parseInt(m.group(MAJOR_GROUP)); final int minor = Integer.parseInt(m.group(MINOR_GROUP)); final int patch = Integer.parseInt(m.group(PATCH_GROUP)); checkParams(major, minor, patch); final String preRelease; if (m.group(PRE_RELEASE_GROUP) != null) { preRelease = m.group(PRE_RELEASE_GROUP); } else { preRelease = ""; } final String buildMD; if (m.group(BUILD_MD_GROUP) != null) { buildMD = m.group(BUILD_MD_GROUP); } else { buildMD = ""; } return new VersionRegEx(major, minor, patch, preRelease, buildMD); } /** * Tries to parse the provided String as a semantic version. If * {@code allowPreRelease} is false, the String must have neither a * pre-release nor a build meta data part. Thus the given String must have the format * {@code X.Y.Z} where at least one part must be greater than zero. * *

* If {@code allowPreRelease} is true, the String is parsed according to * the normal semantic version specification. *

* * @param versionString The String to parse. * @param allowPreRelease Whether pre-release and build meta data field are allowed. * @return The parsed version. * @throws VersionFormatException If the String is no valid version * @since 0.4.0 */ public static VersionRegEx parseVersion(String versionString, boolean allowPreRelease) { final VersionRegEx version = parseVersion(versionString); if (!allowPreRelease && (version.isPreRelease() || version.hasBuildMetaData())) { throw new VersionFormatException(String.format( "Version is expected to have no pre-release or build meta data part")); } return version; } /** * Returns the lower of this version and the given version according to its natural * ordering. If versions are equal, {@code this} is returned. * * @param other The version to compare with. * @return The lower version. * @throws IllegalArgumentException If {@code other} is null. * @since 0.5.0 * @see #min(VersionRegEx, VersionRegEx) */ public VersionRegEx min(VersionRegEx other) { return min(this, other); } /** * Returns the greater of this version and the given version according to its natural * ordering. If versions are equal, {@code this} is returned. * * @param other The version to compare with. * @return The greater version. * @throws IllegalArgumentException If {@code other} is null. * @since 0.5.0 * @see #max(VersionRegEx, VersionRegEx) */ public VersionRegEx max(VersionRegEx other) { return max(this, other); } /** * Gets this version's major number. * * @return The major version. */ public int getMajor() { return this.major; } /** * Gets this version's minor number. * * @return The minor version. */ public int getMinor() { return this.minor; } /** * Gets this version's path number. * * @return The patch number. */ public int getPatch() { return this.patch; } /** * Gets the pre release parts of this version as array by splitting the pre result * version string at the dots. * * @return Pre release version as array. Array is empty if this version has no pre * release part. */ public String[] getPreReleaseParts() { return this.preRelease.isEmpty() ? EMPTY_ARRAY : this.preRelease.split("\\."); } /** * Gets the pre release identifier of this version. If this version has no such * identifier, an empty string is returned. * * @return This version's pre release identifier or an empty String if this version * has no such identifier. */ public String getPreRelease() { return this.preRelease; } /** * Gets this version's build meta data. If this version has no build meta data, the * returned string is empty. * * @return The build meta data or an empty String if this version has no build meta * data. */ public String getBuildMetaData() { return this.buildMetaData; } /** * Gets this version's build meta data as array by splitting the meta data at dots. If * this version has no build meta data, the result is an empty array. * * @return The build meta data as array. */ public String[] getBuildMetaDataParts() { return this.buildMetaData.isEmpty() ? EMPTY_ARRAY : this.buildMetaData.split("\\."); } /** * Determines whether this version is still under initial development. * * @return true iff this version's major part is zero. */ public boolean isInitialDevelopment() { return this.major == 0; } /** * Determines whether this is a pre release version. * * @return true iff {@link #getPreRelease()} is not empty. */ public boolean isPreRelease() { return !this.preRelease.isEmpty(); } /** * Determines whether this version has a build meta data field. * * @return true iff {@link #getBuildMetaData()} is not empty. */ public boolean hasBuildMetaData() { return !this.buildMetaData.isEmpty(); } /** * Creates a String representation of this version by joining its parts together as by * the semantic version specification. * * @return The version as a String. */ @Override public String toString() { final StringBuilder b = new StringBuilder(this.preRelease.length() + this.buildMetaData.length() + TO_STRING_ESTIMATE); b.append(this.major).append(".").append(this.minor).append(".") .append(this.patch); if (!this.preRelease.isEmpty()) { b.append("-").append(this.preRelease); } if (!this.buildMetaData.isEmpty()) { b.append("+").append(this.buildMetaData); } return b.toString(); } /** * The hash code for a version instance is computed from the fields {@link #getMajor() * major}, {@link #getMinor() minor}, {@link #getPatch() patch} and * {@link #getPreRelease() pre-release}. * * @return A hash code for this object. */ @Override public int hashCode() { int h = this.hash; if (h == 0) { h = HASH_PRIME + this.major; h = HASH_PRIME * h + this.minor; h = HASH_PRIME * h + this.patch; h = HASH_PRIME * h + this.preRelease.hashCode(); this.hash = h; } return this.hash; } /** * Determines whether this version is equal to the passed object. This is the case if * the passed object is an instance of Version and this version * {@link #compareTo(VersionRegEx) compared} to the provided one yields 0. Thus, this * method ignores the {@link #getBuildMetaData()} field. * * @param obj the object to compare with. * @return true iff {@code obj} is an instance of {@code Version} and * {@code this.compareTo((Version) obj) == 0} * @see #compareTo(VersionRegEx) */ @Override public boolean equals(Object obj) { return testEquality(obj, false); } /** * Determines whether this version is equal to the passed object (as determined by * {@link #equals(Object)} and additionally considers the build meta data part of both * versions for equality. * * @param obj The object to compare with. * @return true iff {@code this.equals(obj)} and * {@code this.getBuildMetaData().equals(((Version) obj).getBuildMetaData())} * @since 0.4.0 */ public boolean equalsWithBuildMetaData(Object obj) { return testEquality(obj, true); } private boolean testEquality(Object obj, boolean includeBuildMd) { return obj == this || obj instanceof VersionRegEx && compare(this, (VersionRegEx) obj, includeBuildMd) == 0; } /** * Compares this version to the provided one, following the semantic * versioning specification. See {@link #compare(VersionRegEx, VersionRegEx)} for more * information. * * @param other The version to compare to. * @return A value lower than 0 if this < other, a value greater than 0 if this * > other and 0 if this == other. The absolute value of the result has no * semantical interpretation. */ @Override public int compareTo(VersionRegEx other) { return compare(this, other); } /** * Compares this version to the provided one. Unlike the {@link #compareTo(VersionRegEx)} * method, this one additionally considers the build meta data field of both versions, * if all other parts are equal. Note: This is not part of the semantic * version specification. * *

* Comparison of the build meta data parts happens exactly as for pre release * identifiers. Considering of build meta data first kicks in if both versions are * equal when using their natural order. *

* * @param other The version to compare to. * @return A value lower than 0 if this < other, a value greater than 0 if this * > other and 0 if this == other. The absolute value of the result has no * semantical interpretation. * @since 0.3.0 */ public int compareToWithBuildMetaData(VersionRegEx other) { return compareWithBuildMetaData(this, other); } } libsemantic-version-java-2.1.1+ds/src/main/000077500000000000000000000000001445177650100205365ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/main/java/000077500000000000000000000000001445177650100214575ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/main/java/de/000077500000000000000000000000001445177650100220475ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/main/java/de/skuzzle/000077500000000000000000000000001445177650100235565ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/main/java/de/skuzzle/semantic/000077500000000000000000000000001445177650100253615ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/main/java/de/skuzzle/semantic/Version.java000066400000000000000000002130531445177650100276550ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2017 Simon Taddiken * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package de.skuzzle.semantic; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Locale; /** * This class is an implementation of the full semantic version 2.0.0 * specification. Instances can be obtained using the * static overloads of the create method or by {@link #parseVersion(String) * parsing} a String. This class implements {@link Comparable} to compare two versions by * following the specifications linked to above. The {@link #equals(Object)} method * conforms to the result of {@link #compareTo(Version)}, {@link #hashCode()} is * implemented appropriately. Neither method considers the {@link #getBuildMetaData() * build meta data} field for comparison. * *

* Instances of this class are immutable and thus thread safe. This also means that all * methods taking an array or other kind of modifiable objects as input, will first make a * copy before using it as internal state. * *

* Note that unless stated otherwise, none of the public methods of this class accept * null values. Most methods will throw an {@link IllegalArgumentException} * when encountering a null argument. However, to comply with the * {@link Comparable} contract, the comparison methods will throw a * {@link NullPointerException} instead. * * @author Simon Taddiken */ public final class Version implements Comparable, Serializable { /** Conforms to all Version implementations since 0.6.0 */ private static final long serialVersionUID = 6034927062401119911L; private static final String[] EMPTY_ARRAY = new String[0]; /** * The minimum value '0.0.0' for valid versions where all parts are 0 or empty. * * @since 2.1.0 */ public static final Version ZERO = Version.create(0, 0, 0); /** * Semantic Version Specification to which this class complies. * * @since 0.2.0 */ public static final Version COMPLIANCE = Version.create(2, 0, 0); /** * This exception indicates that a version- or a part of a version string could not be * parsed according to the semantic version specification. * * @author Simon Taddiken */ public static class VersionFormatException extends RuntimeException { private static final long serialVersionUID = 1L; /** * Creates a new VersionFormatException with the given message. * * @param message The exception message. */ private VersionFormatException(String message) { super(message); } } /** * Comparator for natural version ordering. See {@link #compare(Version, Version)} for * more information. *

* Instead of using this field, consider using a method reference like in * Version::compare. * * @since 0.2.0 */ public static final Comparator NATURAL_ORDER = new Comparator() { @Override public int compare(Version o1, Version o2) { return o1.compareTo(o2); } }; /** * Comparator for ordering versions with additionally considering the build meta data * field when comparing versions. *

* Instead of using this field, consider using a method reference like in * Version::compareWithBuildMetaData. *

* Note: this comparator imposes orderings that are inconsistent with equals. * * @since 0.3.0 */ public static final Comparator WITH_BUILD_META_DATA_ORDER = new Comparator() { @Override public int compare(Version o1, Version o2) { return o1.compareToWithBuildMetaData(o2); } }; private static final int TO_STRING_ESTIMATE = 16; // state machine states for parsing a version string private static final int STATE_MAJOR_INIT = 0; private static final int STATE_MAJOR_LEADING_ZERO = 1; private static final int STATE_MAJOR_DEFAULT = 2; private static final int STATE_MINOR_INIT = 3; private static final int STATE_MINOR_LEADING_ZERO = 4; private static final int STATE_MINOR_DEFAULT = 5; private static final int STATE_PATCH_INIT = 6; private static final int STATE_PATCH_LEADING_ZERO = 7; private static final int STATE_PATCH_DEFAULT = 8; private static final int STATE_PRERELEASE_INIT = 9; private static final int STATE_BUILDMD_INIT = 10; private static final int STATE_PART_INIT = 0; private static final int STATE_PART_LEADING_ZERO = 1; private static final int STATE_PART_NUMERIC = 2; private static final int STATE_PART_DEFAULT = 3; private static final int DECIMAL = 10; private static final int EOS = -1; private static final int FAILURE = -2; private final int major; private final int minor; private final int patch; private final String[] preReleaseParts; private final String[] buildMetaDataParts; // Since 1.1.0 // these fields are only necessary for deserializing previous versions // see #readResolve method @Deprecated private String preRelease; @Deprecated private String buildMetaData; // store hash code once it has been calculated private static final int NOT_YET_CALCULATED = 2; private static final int HASH_PRIME = 31; private volatile int hash = NOT_YET_CALCULATED; private Version(int major, int minor, int patch, String[] preRelease, String[] buildMd) { checkParams(major, minor, patch); this.major = major; this.minor = minor; this.patch = patch; this.preReleaseParts = preRelease; this.buildMetaDataParts = buildMd; } private static Version parse(String s, boolean verifyOnly) { /* * Since 1.1.0: * * This huge and ugly inline parsing replaces the prior regex because it is way * faster. Besides that it also does provide better error messages in case a * String could not be parsed correctly. Condition and mutation coverage is * extremely high to ensure correctness. */ // note: getting the char array once is faster than calling charAt multiple times final char[] stream = s.toCharArray(); int major = 0; int minor = 0; int patch = 0; int state = STATE_MAJOR_INIT; List preRelease = null; List buildMd = null; loop: for (int i = 0; i <= stream.length; ++i) { final int c = i < stream.length ? stream[i] : EOS; switch (state) { // Parse major part case STATE_MAJOR_INIT: if (c == '0') { state = STATE_MAJOR_LEADING_ZERO; } else if (c >= '1' && c <= '9') { major = major * DECIMAL + Character.digit(c, DECIMAL); state = STATE_MAJOR_DEFAULT; } else if (verifyOnly) { return null; } else { throw unexpectedChar(s, c); } break; case STATE_MAJOR_LEADING_ZERO: if (c == '.') { // single 0 is allowed state = STATE_MINOR_INIT; } else if (c >= '0' && c <= '9') { if (verifyOnly) { return null; } throw illegalLeadingChar(s, '0', "major"); } else if (verifyOnly) { return null; } else { throw unexpectedChar(s, c); } break; case STATE_MAJOR_DEFAULT: if (c >= '0' && c <= '9') { major = major * DECIMAL + Character.digit(c, DECIMAL); } else if (c == '.') { state = STATE_MINOR_INIT; } else if (verifyOnly) { return null; } else { throw unexpectedChar(s, c); } break; // parse minor part case STATE_MINOR_INIT: if (c == '0') { state = STATE_MINOR_LEADING_ZERO; } else if (c >= '1' && c <= '9') { minor = minor * DECIMAL + Character.digit(c, DECIMAL); state = STATE_MINOR_DEFAULT; } else if (verifyOnly) { return null; } else { throw unexpectedChar(s, c); } break; case STATE_MINOR_LEADING_ZERO: if (c == '.') { // single 0 is allowed state = STATE_PATCH_INIT; } else if (c >= '0' && c <= '9') { if (verifyOnly) { return null; } throw illegalLeadingChar(s, '0', "minor"); } else if (verifyOnly) { return null; } else { throw unexpectedChar(s, c); } break; case STATE_MINOR_DEFAULT: if (c >= '0' && c <= '9') { minor = minor * DECIMAL + Character.digit(c, DECIMAL); } else if (c == '.') { state = STATE_PATCH_INIT; } else if (verifyOnly) { return null; } else { throw unexpectedChar(s, c); } break; // parse patch part case STATE_PATCH_INIT: if (c == '0') { state = STATE_PATCH_LEADING_ZERO; } else if (c >= '1' && c <= '9') { patch = patch * DECIMAL + Character.digit(c, DECIMAL); state = STATE_PATCH_DEFAULT; } else if (verifyOnly) { return null; } else { throw unexpectedChar(s, c); } break; case STATE_PATCH_LEADING_ZERO: if (c == '-') { // single 0 is allowed state = STATE_PRERELEASE_INIT; } else if (c == '+') { state = STATE_BUILDMD_INIT; } else if (c == EOS) { break loop; } else if (c >= '0' && c <= '9') { if (verifyOnly) { return null; } throw illegalLeadingChar(s, '0', "patch"); } else if (verifyOnly) { return null; } else { throw unexpectedChar(s, c); } break; case STATE_PATCH_DEFAULT: if (c >= '0' && c <= '9') { patch = patch * DECIMAL + Character.digit(c, DECIMAL); } else if (c == '-') { state = STATE_PRERELEASE_INIT; } else if (c == '+') { state = STATE_BUILDMD_INIT; } else if (c != EOS) { // eos is allowed here if (verifyOnly) { return null; } throw unexpectedChar(s, c); } break; case STATE_PRERELEASE_INIT: preRelease = verifyOnly ? null : new ArrayList(); i = parseID(stream, s, i, verifyOnly, false, true, preRelease, "pre-release"); if (i == FAILURE) { // implies verifyOnly == true, otherwise exception would have been // thrown return null; } final int c1 = i < stream.length ? stream[i] : EOS; if (c1 == '+') { state = STATE_BUILDMD_INIT; } else { break loop; } break; case STATE_BUILDMD_INIT: buildMd = verifyOnly ? null : new ArrayList(); i = parseID(stream, s, i, verifyOnly, true, false, buildMd, "build-meta-data"); if (i == FAILURE) { // implies verifyOnly == true, otherwise exception would have been // thrown return null; } break loop; default: throw new IllegalStateException("Illegal state: " + state); } } final String[] prerelease = preRelease == null ? EMPTY_ARRAY : preRelease.toArray(new String[preRelease.size()]); final String[] buildmetadata = buildMd == null ? EMPTY_ARRAY : buildMd.toArray(new String[buildMd.size()]); return new Version(major, minor, patch, prerelease, buildmetadata); } private static int parseID(char[] stream, String full, int start, boolean verifyOnly, boolean allowLeading0, boolean preRelease, List parts, String partName) { assert verifyOnly || parts != null; final StringBuilder b = verifyOnly ? null : new StringBuilder(stream.length - start); int i = start; while (i <= stream.length) { i = parseIDPart(stream, full, i, verifyOnly, allowLeading0, preRelease, true, b, partName); if (i == FAILURE) { // implies verifyOnly == true, otherwise exception would have been thrown return FAILURE; } else if (!verifyOnly) { parts.add(b.toString()); } final int c = i < stream.length ? stream[i] : EOS; if (c == '.') { // keep looping ++i; } else { // identifier is done (hit EOS or '+') return i; } } throw new IllegalStateException(); } private static int parseIDPart(char[] stream, String full, int start, boolean verifyOnly, boolean allowLeading0, boolean preRelease, boolean allowDot, StringBuilder b, String partName) { if (b != null) { b.setLength(0); } int state = STATE_PART_INIT; for (int i = start; i <= stream.length; ++i) { final int c = i < stream.length ? stream[i] : EOS; switch (state) { case STATE_PART_INIT: if (c == '0' && !allowLeading0) { state = STATE_PART_LEADING_ZERO; if (b != null) { b.append('0'); } } else if (c == '-' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9') { if (b != null) { b.appendCodePoint(c); } state = STATE_PART_DEFAULT; } else if (c == '.') { if (verifyOnly) { return FAILURE; } throw unexpectedChar(full, -1); } else { if (verifyOnly) { return FAILURE; } throw unexpectedChar(full, c); } break; case STATE_PART_LEADING_ZERO: // when in this state we consumed a single '0' if (c == '-' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') { if (b != null) { b.appendCodePoint(c); } state = STATE_PART_DEFAULT; } else if (c >= '0' && c <= '9') { if (b != null) { b.appendCodePoint(c); } state = STATE_PART_NUMERIC; } else if (c == '.' && allowDot || c == EOS || c == '+' && preRelease) { // if we are parsing a pre release part it can be terminated by a // '+' in case a build meta data follows // here, this part consist of a single '0' return i; } else if (verifyOnly) { return FAILURE; } else { throw unexpectedChar(full, c); } break; case STATE_PART_NUMERIC: // when in this state, the part began with a '0' and we only consumed // numeric chars so far if (c == '-' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') { if (b != null) { b.appendCodePoint(c); } state = STATE_PART_DEFAULT; } else if (c >= '0' && c <= '9') { if (b != null) { b.appendCodePoint(c); } } else if (c == '.' || c == EOS || c == '+' && preRelease) { // if we are parsing a pre release part it can be terminated by a // '+' in case a build meta data follows if (verifyOnly) { return FAILURE; } throw illegalLeadingChar(full, '0', partName); } else if (verifyOnly) { return FAILURE; } else { throw unexpectedChar(full, c); } break; case STATE_PART_DEFAULT: if (c == '-' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9') { if (b != null) { b.appendCodePoint(c); } } else if (c == '.' && allowDot || c == EOS || c == '+' && preRelease) { // if we are parsing a pre release part it can be terminated by a // '+' in case a build meta data follows return i; } else if (verifyOnly) { return FAILURE; } else { throw unexpectedChar(full, c); } break; } } throw new IllegalStateException(); } private static VersionFormatException illegalLeadingChar(String v, int c, String part) { return new VersionFormatException( String.format("Illegal leading char '%c' in %s part of %s", c, part, v)); } private static VersionFormatException unexpectedChar(String v, int c) { if (c == EOS) { return new VersionFormatException(String.format( "Incomplete version part in %s", v)); } return new VersionFormatException( String.format("Unexpected char in %s: %c", v, c)); } /** * Creates a new Version from this one, replacing only the major part with the given * one. All other parts will remain the same as in this Version. * * @param newMajor The new major version. * @return A new Version. * @throws IllegalArgumentException If all fields in the resulting Version are 0. * @since 1.1.0 */ public Version withMajor(int newMajor) { return new Version(newMajor, this.minor, this.patch, this.preReleaseParts, this.buildMetaDataParts); } /** * Creates a new Version from this one, replacing only the minor part with the given * one. All other parts will remain the same as in this Version. * * @param newMinor The new minor version. * @return A new Version. * @throws IllegalArgumentException If all fields in the resulting Version are 0. * @since 1.1.0 */ public Version withMinor(int newMinor) { return new Version(this.major, newMinor, this.patch, this.preReleaseParts, this.buildMetaDataParts); } /** * Creates a new Version from this one, replacing only the patch part with the given * one. All other parts will remain the same as in this Version. * * @param newPatch The new patch version. * @return A new Version. * @throws IllegalArgumentException If all fields in the resulting Version are 0. * @since 1.1.0 */ public Version withPatch(int newPatch) { return new Version(this.major, this.minor, newPatch, this.preReleaseParts, this.buildMetaDataParts); } /** * Creates a new Version from this one, replacing only the pre-release part with the * given String. All other parts will remain the same as in this Version. You can * remove the pre-release part by passing an empty String. * * @param newPreRelease The new pre-release identifier. * @return A new Version. * @throws VersionFormatException If the given String is not a valid pre-release * identifier. * @throws IllegalArgumentException If preRelease is null. * @since 1.1.0 */ public Version withPreRelease(String newPreRelease) { require(newPreRelease != null, "newPreRelease is null"); final String[] newPreReleaseParts = parsePreRelease(newPreRelease); return new Version(this.major, this.minor, this.patch, newPreReleaseParts, this.buildMetaDataParts); } /** * Creates a new Version from this one, replacing only the pre-release part with the * given array. All other parts will remain the same as in this Version. You can * remove the pre-release part by passing an empty array. *

* The passed array will be copied to not allow external modification to the new * Version's inner state. * *

* A single part within the array is allowed to contain a dot ('.'). Such parts will * be treated as if the array contained those parts as single elements. * * *

     * v.withPreRelease(new String[] { "a.b" })
     * <=>
     * v.withPreRelease(new String[] { "a", "b" })
     * 
* * @param newPreRelease the new pre release parts. * @return A new Version. * @throws VersionFormatException If the any element of the given array is not a valid * pre release identifier part. * @throws IllegalArgumentException If newPreRelease is null. * @since 1.2.0 */ public Version withPreRelease(String[] newPreRelease) { require(newPreRelease != null, "newPreRelease is null"); final String joined = join(newPreRelease); final String[] newPreReleaseParts = parsePreRelease(joined); return new Version(this.major, this.minor, this.patch, newPreReleaseParts, this.buildMetaDataParts); } /** * Creates a new Version from this one, replacing only the build-meta-data part with * the given String. All other parts will remain the same as in this Version. You can * remove the build-meta-data part by passing an empty String. * * @param newBuildMetaData The new build meta data identifier. * @return A new Version. * @throws VersionFormatException If the given String is not a valid build-meta-data * identifier. * @throws IllegalArgumentException If newBuildMetaData is null. * @since 1.1.0 */ public Version withBuildMetaData(String newBuildMetaData) { require(newBuildMetaData != null, "newBuildMetaData is null"); final String[] newBuildMdParts = parseBuildMd(newBuildMetaData); return new Version(this.major, this.minor, this.patch, this.preReleaseParts, newBuildMdParts); } /** * Creates a new Version from this one, replacing only the build-meta-data part with * the given array. All other parts will remain the same as in this Version. You can * remove the build-meta-data part by passing an empty array. *

* The passed array will be copied to not allow external modification to the new * Version's inner state. * * A single part within the array is allowed to contain a dot ('.'). Such parts will * be treated as if the array contained those parts as single elements. * * *

     * v.withBuildMetaData(new String[] { "a.b" })
     * <=>
     * v.withBuildMetaData(new String[] { "a", "b" })
     * 
* * @param newBuildMetaData the new build meta data parts. * @return A new Version. * @throws VersionFormatException If the any element of the given array is not a valid * build meta data identifier part. * @throws IllegalArgumentException If newBuildMetaData is null. * @since 1.2.0 */ public Version withBuildMetaData(String[] newBuildMetaData) { require(newBuildMetaData != null, "newBuildMetaData is null"); final String joined = join(newBuildMetaData); final String[] newBuildMdParts = parseBuildMd(joined); return new Version(this.major, this.minor, this.patch, this.preReleaseParts, newBuildMdParts); } private String[] verifyAndCopyArray(String parts[], boolean allowLeading0) { final String[] result = new String[parts.length]; for (int i = 0; i < parts.length; ++i) { final String part = parts[i]; require(part != null, "version part is null"); if (part.isEmpty()) { throw new VersionFormatException( "Incomplete version part in " + join(parts)); } result[i] = part; // note: pass "pre-release" because this string will not be used when parsing // build-meta-data parseIDPart(part.toCharArray(), part, 0, false, allowLeading0, false, false, null, "pre-release"); } return result; } /** * Drops both the pre-release and the build meta data from this version. * * @return The nearest stable version. * @since 2.1.0 */ public Version toStable() { return new Version(this.major, this.minor, this.patch, EMPTY_ARRAY, EMPTY_ARRAY); } /** * Given this Version, returns the next major Version. That is, the major part is * incremented by 1 and the remaining parts are set to 0. This also drops the * pre-release and build-meta-data. * * @return The incremented version. * @see #nextMajor(String) * @see #nextMajor(String[]) * @since 1.2.0 */ public Version nextMajor() { return new Version(this.major + 1, 0, 0, EMPTY_ARRAY, EMPTY_ARRAY); } /** * Given this Version, returns the next major Version. That is, the major part is * incremented by 1 and the remaining parts are set to 0. The pre-release part will be * set to the given identifier and the build-meta-data is dropped. * * @param newPrelease The pre-release part for the resulting Version. * @return The incremented version. * @throws VersionFormatException If the given String is not a valid pre-release * identifier. * @throws IllegalArgumentException If newPreRelease is null. * @see #nextMajor() * @see #nextMajor(String[]) * @since 1.2.0 */ public Version nextMajor(String newPrelease) { require(newPrelease != null, "newPreRelease is null"); final String[] preReleaseParts = parsePreRelease(newPrelease); return new Version(this.major + 1, 0, 0, preReleaseParts, EMPTY_ARRAY); } /** * Given this Version, returns the next major Version. That is, the major part is * incremented by 1 and the remaining parts are set to 0. The pre-release part will be * set to the given identifier and the build-meta-data is dropped. * * @param newPrelease The pre-release part for the resulting Version. * @return The incremented version. * @throws VersionFormatException If the any element of the given array is not a valid * pre release identifier part. * @throws IllegalArgumentException If newPreRelease is null. * @see #nextMajor() * @see #nextMajor(String) * @since 1.2.0 */ public Version nextMajor(String[] newPrelease) { require(newPrelease != null, "newPreRelease is null"); final String[] newPreReleaseParts = verifyAndCopyArray(newPrelease, false); return new Version(this.major + 1, 0, 0, newPreReleaseParts, EMPTY_ARRAY); } /** * Given this version, returns the next minor version. That is, the major part remains * the same, the minor version is incremented and all other parts are reset/dropped. * * @return The incremented version. * @see #nextMinor(String) * @see #nextMinor(String[]) * @since 1.2.0 */ public Version nextMinor() { return new Version(this.major, this.minor + 1, 0, EMPTY_ARRAY, EMPTY_ARRAY); } /** * Given this version, returns the next minor version. That is, the major part remains * the same and the minor version is incremented. The pre-release part will be set to * the given identifier and the build-meta-data is dropped. * * @param newPrelease The pre-release part for the resulting Version. * @return The incremented version. * @throws VersionFormatException If the given String is not a valid pre-release * identifier. * @throws IllegalArgumentException If newPreRelease is null. * @see #nextMinor() * @see #nextMinor(String[]) * @since 1.2.0 */ public Version nextMinor(String newPrelease) { require(newPrelease != null, "newPreRelease is null"); final String[] preReleaseParts = parsePreRelease(newPrelease); return new Version(this.major, this.minor + 1, 0, preReleaseParts, EMPTY_ARRAY); } /** * Given this version, returns the next minor version. That is, the major part remains * the same and the minor version is incremented. The pre-release part will be set to * the given identifier and the build-meta-data is dropped. * * @param newPrelease The pre-release part for the resulting Version. * @return The incremented version. * @throws VersionFormatException If the any element of the given array is not a valid * pre release identifier part. * @throws IllegalArgumentException If newPreRelease is null. * @see #nextMinor() * @see #nextMinor(String) * @since 1.2.0 */ public Version nextMinor(String[] newPrelease) { require(newPrelease != null, "newPreRelease is null"); final String[] newPreReleaseParts = verifyAndCopyArray(newPrelease, false); return new Version(this.major, this.minor + 1, 0, newPreReleaseParts, EMPTY_ARRAY); } /** * Given this version, returns the next patch version. That is, the major and minor * parts remain the same, the patch version is incremented and all other parts are * reset/dropped. * * @return The incremented version. * @see #nextPatch(String) * @see #nextPatch(String[]) * @since 1.2.0 */ public Version nextPatch() { return new Version(this.major, this.minor, this.patch + 1, EMPTY_ARRAY, EMPTY_ARRAY); } /** * Given this version, returns the next patch version. That is, the major and minor * parts remain the same and the patch version is incremented. The pre-release part * will be set to the given identifier and the build-meta-data is dropped. * * @param newPrelease The pre-release part for the resulting Version. * @return The incremented version. * @throws VersionFormatException If the given String is not a valid pre-release * identifier. * @throws IllegalArgumentException If newPreRelease is null. * @see #nextPatch() * @see #nextPatch(String[]) * @since 1.2.0 */ public Version nextPatch(String newPrelease) { require(newPrelease != null, "newPreRelease is null"); final String[] preReleaseParts = parsePreRelease(newPrelease); return new Version(this.major, this.minor, this.patch + 1, preReleaseParts, EMPTY_ARRAY); } /** * Given this version, returns the next patch version. That is, the major and minor * parts remain the same and the patch version is incremented. The pre-release part * will be set to the given identifier and the build-meta-data is dropped. * * @param newPrelease The pre-release part for the resulting Version. * @return The incremented version. * @throws VersionFormatException If the any element of the given array is not a valid * pre release identifier part. * @throws IllegalArgumentException If newPreRelease is null. * @see #nextPatch() * @see #nextPatch(String) * @since 1.2.0 */ public Version nextPatch(String[] newPrelease) { require(newPrelease != null, "newPreRelease is null"); final String[] newPreReleaseParts = verifyAndCopyArray(newPrelease, false); return new Version(this.major, this.minor, this.patch + 1, newPreReleaseParts, EMPTY_ARRAY); } /** * Derives a new Version instance from this one by only incrementing the pre-release * identifier. The build-meta-data will be dropped, all other fields remain the same. * *

* The incrementation of the pre-release identifier behaves as follows: * *

    *
  • In case the identifier is currently empty, it becomes "1" in the result.
  • *
  • If the identifier's last part is numeric, that last part will be incremented in * the result.
  • *
  • If the last part is not numeric, the identifier is interpreted as * {@code identifier.0} which becomes {@code identifier.1} after increment. *
* Examples: * * * * * * * * * * * * * * * * * * * * * * * *
Pre-release identifier incrementation behavior
VersionAfter increment
1.2.31.2.3-1
1.2.3+build.meta.data1.2.3-1
1.2.3-foo1.2.3-foo.1
1.2.3-foo.11.2.3-foo.2
* * @return The incremented Version. * @since 1.2.0 */ public Version nextPreRelease() { final String[] newPreReleaseParts = incrementIdentifier(this.preReleaseParts); return new Version(this.major, this.minor, this.patch, newPreReleaseParts, EMPTY_ARRAY); } /** * Derives a new Version instance from this one by only incrementing the * build-meta-data identifier. All other fields remain the same. * *

* The incrementation of the build-meta-data identifier behaves as follows: * *

    *
  • In case the identifier is currently empty, it becomes "1" in the result.
  • *
  • If the identifier's last part is numeric, that last part will be incremented in * the result. Leading 0's will be removed.
  • *
  • If the last part is not numeric, the identifier is interpreted as * {@code identifier.0} which becomes {@code identifier.1} after increment. *
* Examples: * * * * * * * * * * * * * * * * * * * * * * * *
Build meta data incrementation behavior
VersionAfter increment
1.2.31.2.3+1
1.2.3-pre.release1.2.3-pre.release+1
1.2.3+foo1.2.3+foo.1
1.2.3+foo.11.2.3+foo.2
* * @return The incremented Version. * @since 1.2.0 */ public Version nextBuildMetaData() { final String[] newBuildMetaData = incrementIdentifier(this.buildMetaDataParts); return new Version(this.major, this.minor, this.patch, this.preReleaseParts, newBuildMetaData); } private String[] incrementIdentifier(String[] parts) { if (parts.length == 0) { return new String[] { "1" }; } final int lastIdx = parts.length - 1; final String lastPart = parts[lastIdx]; int num = isNumeric(lastPart); int newLength = parts.length; if (num >= 0) { num += 1; } else { newLength += 1; num = 1; } final String[] result = Arrays.copyOf(parts, newLength); result[newLength - 1] = String.valueOf(num); return result; } /** * Tries to parse the given String as a semantic version and returns whether the * String is properly formatted according to the semantic version specification. * *

* Note: this method does not throw an exception upon null input, but * returns false instead. * * * @param version The String to check. * @return Whether the given String is formatted as a semantic version. * @since 0.5.0 */ public static boolean isValidVersion(String version) { return version != null && !version.isEmpty() && parse(version, true) != null; } /** * Returns whether the given String is a valid pre-release identifier. That is, this * method returns true if, and only if the {@code preRelease} parameter * is either the empty string or properly formatted as a pre-release identifier * according to the semantic version specification. * *

* Note: this method does not throw an exception upon null input, but * returns false instead. * * @param preRelease The String to check. * @return Whether the given String is a valid pre-release identifier. * @since 0.5.0 */ public static boolean isValidPreRelease(String preRelease) { if (preRelease == null) { return false; } else if (preRelease.isEmpty()) { return true; } return parseID(preRelease.toCharArray(), preRelease, 0, true, false, false, null, "") != FAILURE; } /** * Returns whether the given String is a valid build meta data identifier. That is, * this method returns true if, and only if the {@code buildMetaData} * parameter is either the empty string or properly formatted as a build meta data * identifier according to the semantic version specification. * *

* Note: this method does not throw an exception upon null input, but * returns false instead. * * * @param buildMetaData The String to check. * @return Whether the given String is a valid build meta data identifier. * @since 0.5.0 */ public static boolean isValidBuildMetaData(String buildMetaData) { if (buildMetaData == null) { return false; } else if (buildMetaData.isEmpty()) { return true; } return parseID(buildMetaData.toCharArray(), buildMetaData, 0, true, true, false, null, "") != FAILURE; } /** * Returns the greater of the two given versions by comparing them using their natural * ordering. If both versions are equal, then the first argument is returned. * * @param v1 The first version. * @param v2 The second version. * @return The greater version. * @throws IllegalArgumentException If either argument is null. * @since 0.4.0 */ public static Version max(Version v1, Version v2) { require(v1 != null, "v1 is null"); require(v2 != null, "v2 is null"); return compare(v1, v2, false) < 0 ? v2 : v1; } /** * Returns the lower of the two given versions by comparing them using their natural * ordering. If both versions are equal, then the first argument is returned. * * @param v1 The first version. * @param v2 The second version. * @return The lower version. * @throws IllegalArgumentException If either argument is null. * @since 0.4.0 */ public static Version min(Version v1, Version v2) { require(v1 != null, "v1 is null"); require(v2 != null, "v2 is null"); return compare(v1, v2, false) <= 0 ? v1 : v2; } /** * Compares two versions, following the semantic version specification. Here * is a quote from semantic version 2.0.0 * specification: * *

* Precedence refers to how versions are compared to each other when ordered. * Precedence MUST be calculated by separating the version into major, minor, patch * and pre-release identifiers in that order (Build metadata does not figure into * precedence). Precedence is determined by the first difference when comparing each * of these identifiers from left to right as follows: Major, minor, and patch * versions are always compared numerically. Example: 1.0.0 < 2.0.0 < 2.1.0 < * 2.1.1. When major, minor, and patch are equal, a pre-release version has lower * precedence than a normal version. Example: 1.0.0-alpha < 1.0.0. Precedence for * two pre-release versions with the same major, minor, and patch version MUST be * determined by comparing each dot separated identifier from left to right until a * difference is found as follows: identifiers consisting of only digits are compared * numerically and identifiers with letters or hyphens are compared lexically in ASCII * sort order. Numeric identifiers always have lower precedence than non-numeric * identifiers. A larger set of pre-release fields has a higher precedence than a * smaller set, if all of the preceding identifiers are equal. 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. * * *

* This method fulfills the general contract for Java's {@link Comparator Comparators} * and {@link Comparable Comparables}. * * * @param v1 The first version for comparison. * @param v2 The second version for comparison. * @return A value below 0 iff {@code v1 < v2}, a value above 0 iff * {@code v1 > v2 and 0 iff v1 = v2}. * @throws NullPointerException If either parameter is null. * @since 0.2.0 */ public static int compare(Version v1, Version v2) { // throw NPE to comply with Comparable specification if (v1 == null) { throw new NullPointerException("v1 is null"); } else if (v2 == null) { throw new NullPointerException("v2 is null"); } return compare(v1, v2, false); } /** * Compares two Versions with additionally considering the build meta data field if * all other parts are equal. Note: This is not part of the semantic version * specification. * *

* Comparison of the build meta data parts happens exactly as for pre release * identifiers. Considering of build meta data first kicks in if both versions are * equal when using their natural order. * * *

* This method fulfills the general contract for Java's {@link Comparator Comparators} * and {@link Comparable Comparables}. * * * @param v1 The first version for comparison. * @param v2 The second version for comparison. * @return A value below 0 iff {@code v1 < v2}, a value above 0 iff * {@code v1 > v2 and 0 iff v1 = v2}. * @throws NullPointerException If either parameter is null. * @since 0.3.0 */ public static int compareWithBuildMetaData(Version v1, Version v2) { // throw NPE to comply with Comparable specification if (v1 == null) { throw new NullPointerException("v1 is null"); } else if (v2 == null) { throw new NullPointerException("v2 is null"); } return compare(v1, v2, true); } private static int compare(Version v1, Version v2, boolean withBuildMetaData) { int result = 0; if (v1 != v2) { final int mc, mm, mp, pr, md; if ((mc = compareInt(v1.major, v2.major)) != 0) { result = mc; } else if ((mm = compareInt(v1.minor, v2.minor)) != 0) { result = mm; } else if ((mp = compareInt(v1.patch, v2.patch)) != 0) { result = mp; } else if ((pr = comparePreRelease(v1, v2)) != 0) { result = pr; } else if (withBuildMetaData && ((md = compareBuildMetaData(v1, v2)) != 0)) { result = md; } } return result; } private static int compareInt(int a, int b) { return a - b; } private static int comparePreRelease(Version v1, Version v2) { return compareLiterals(v1.preReleaseParts, v2.preReleaseParts); } private static int compareBuildMetaData(Version v1, Version v2) { return compareLiterals(v1.buildMetaDataParts, v2.buildMetaDataParts); } private static int compareLiterals(String[] v1Literal, String[] v2Literal) { final int result; if (v1Literal.length > 0 && v2Literal.length > 0) { // compare pre release parts result = compareIdentifiers(v1Literal, v2Literal); } else if (v1Literal.length > 0) { // other is greater, because it is no pre release result = -1; } else if (v2Literal.length > 0) { // this is greater because other is no pre release result = 1; } else { result = 0; } return result; } private static int compareIdentifiers(String[] parts1, String[] parts2) { final int min = Math.min(parts1.length, parts2.length); for (int i = 0; i < min; ++i) { final int r = compareIdentifierParts(parts1[i], parts2[i]); if (r != 0) { // versions differ in part i return r; } } // all id's are equal, so compare amount of id's return compareInt(parts1.length, parts2.length); } private static int compareIdentifierParts(String p1, String p2) { final int num1 = isNumeric(p1); final int num2 = isNumeric(p2); final int result; if (num1 < 0 && num2 < 0) { // both are not numerical -> compare lexically result = p1.compareTo(p2); } else if (num1 >= 0 && num2 >= 0) { // both are numerical result = compareInt(num1, num2); } else if (num1 >= 0) { // only part1 is numerical -> p2 is greater result = -1; } else { // only part2 is numerical -> p1 is greater result = 1; } return result; } /** * Determines whether s is a positive number. If it is, the number is returned, * otherwise the result is -1. * * @param s The String to check. * @return The positive number (incl. 0) if s a number, or -1 if it is not. */ private static int isNumeric(String s) { final char[] chars = s.toCharArray(); int num = 0; // note: this method does not account for leading zeroes as could occur in build // meta data parts. Leading zeroes are thus simply ignored when parsing the // number. for (int i = 0; i < chars.length; ++i) { final char c = chars[i]; if (c >= '0' && c <= '9') { num = num * DECIMAL + Character.digit(c, DECIMAL); } else { return -1; } } return num; } private static String[] parsePreRelease(String preRelease) { if (preRelease != null && !preRelease.isEmpty()) { final List parts = new ArrayList(); parseID(preRelease.toCharArray(), preRelease, 0, false, false, false, parts, "pre-release"); return parts.toArray(new String[parts.size()]); } return EMPTY_ARRAY; } private static String[] parseBuildMd(String buildMetaData) { if (buildMetaData != null && !buildMetaData.isEmpty()) { final List parts = new ArrayList(); parseID(buildMetaData.toCharArray(), buildMetaData, 0, false, true, false, parts, "build-meta-data"); return parts.toArray(new String[parts.size()]); } return EMPTY_ARRAY; } private static final Version createInternal(int major, int minor, int patch, String preRelease, String buildMetaData) { final String[] preReleaseParts = parsePreRelease(preRelease); final String[] buildMdParts = parseBuildMd(buildMetaData); return new Version(major, minor, patch, preReleaseParts, buildMdParts); } /** * Creates a new Version from the provided components. Neither value of * {@code major, minor} or {@code patch} must be lower than 0 and at least one must be * greater than zero. {@code preRelease} or {@code buildMetaData} may be the empty * String. In this case, the created {@code Version} will have no pre release resp. * build meta data field. If those parameters are not empty, they must conform to the * semantic version specification. * * @param major The major version. * @param minor The minor version. * @param patch The patch version. * @param preRelease The pre release version or the empty string. * @param buildMetaData The build meta data field or the empty string. * @return The version instance. * @throws VersionFormatException If {@code preRelease} or {@code buildMetaData} does * not conform to the semantic version specification. */ public static final Version create(int major, int minor, int patch, String preRelease, String buildMetaData) { require(preRelease != null, "preRelease is null"); require(buildMetaData != null, "buildMetaData is null"); return createInternal(major, minor, patch, preRelease, buildMetaData); } /** * Creates a new Version from the provided components. The version's build meta data * field will be empty. Neither value of {@code major, minor} or {@code patch} must be * lower than 0 and at least one must be greater than zero. {@code preRelease} may be * the empty String. In this case, the created version will have no pre release field. * If it is not empty, it must conform to the specifications of the semantic version. * * @param major The major version. * @param minor The minor version. * @param patch The patch version. * @param preRelease The pre release version or the empty string. * @return The version instance. * @throws VersionFormatException If {@code preRelease} is not empty and does not * conform to the semantic versioning specification */ public static final Version create(int major, int minor, int patch, String preRelease) { return create(major, minor, patch, preRelease, ""); } /** * Creates a new Version from the three provided components. The version's pre release * and build meta data fields will be empty. Neither value must be lower than 0 and at * least one must be greater than zero. * * @param major The major version. * @param minor The minor version. * @param patch The patch version. * @return The version instance. */ public static final Version create(int major, int minor, int patch) { return new Version(major, minor, patch, EMPTY_ARRAY, EMPTY_ARRAY); } /** * Creates a new Version from the two provided components, leaving the patch version * 0. The version's pre release and build meta data fields will be empty. Neither * value must be lower than 0 and at least one must be greater than zero. * * @param major The major version. * @param minor The minor version. * @return The version instance. * @since 2.1.0 */ public static final Version create(int major, int minor) { return new Version(major, minor, 0, EMPTY_ARRAY, EMPTY_ARRAY); } /** * Creates a new Version with the provided major version, leaving the minor and patch * version 0. The version's pre release and build meta data fields will be empty. The * major value must be lower than or equal to 0. * * @param major The major version. * @return The version instance. * @since 2.1.0 */ public static final Version create(int major) { return new Version(major, 0, 0, EMPTY_ARRAY, EMPTY_ARRAY); } private static void checkParams(int major, int minor, int patch) { require(major >= 0, "major < 0"); require(minor >= 0, "minor < 0"); require(patch >= 0, "patch < 0"); } private static void require(boolean condition, String message) { if (!condition) { throw new IllegalArgumentException(message); } } /** * Tries to parse the provided String as a semantic version. If the string does not * conform to the semantic version specification, a {@link VersionFormatException} * will be thrown. * * @param versionString The String to parse. * @return The parsed version. * @throws VersionFormatException If the String is no valid version * @throws IllegalArgumentException If {@code versionString} is null. */ public static final Version parseVersion(String versionString) { require(versionString != null, "versionString is null"); return parse(versionString, false); } /** * Tries to parse the provided String as a semantic version. If * {@code allowPreRelease} is false, the String must have neither a * pre-release nor a build meta data part. Thus the given String must have the format * {@code X.Y.Z} where at least one part must be greater than zero. * *

* If {@code allowPreRelease} is true, the String is parsed according to * the normal semantic version specification. * * * @param versionString The String to parse. * @param allowPreRelease Whether pre-release and build meta data field are allowed. * @return The parsed version. * @throws VersionFormatException If the String is no valid version * @since 0.4.0 */ public static Version parseVersion(String versionString, boolean allowPreRelease) { final Version version = parseVersion(versionString); if (!allowPreRelease && (version.isPreRelease() || version.hasBuildMetaData())) { throw new VersionFormatException(String.format( "Version string '%s' is expected to have no pre-release or " + "build meta data part", versionString)); } return version; } /** * Returns the lower of this version and the given version according to its natural * ordering. If versions are equal, {@code this} is returned. * * @param other The version to compare with. * @return The lower version. * @throws IllegalArgumentException If {@code other} is null. * @since 0.5.0 * @see #min(Version, Version) */ public Version min(Version other) { return min(this, other); } /** * Returns the greater of this version and the given version according to its natural * ordering. If versions are equal, {@code this} is returned. * * @param other The version to compare with. * @return The greater version. * @throws IllegalArgumentException If {@code other} is null. * @since 0.5.0 * @see #max(Version, Version) */ public Version max(Version other) { return max(this, other); } /** * Gets this version's major number. * * @return The major version. */ public int getMajor() { return this.major; } /** * Gets this version's minor number. * * @return The minor version. */ public int getMinor() { return this.minor; } /** * Gets this version's path number. * * @return The patch number. */ public int getPatch() { return this.patch; } /** * Gets the pre release identifier parts of this version as array. Modifying the * resulting array will have no influence on the internal state of this object. * * @return Pre release version as array. Array is empty if this version has no pre * release part. */ public String[] getPreReleaseParts() { if (this.preReleaseParts.length == 0) { return EMPTY_ARRAY; } return Arrays.copyOf(this.preReleaseParts, this.preReleaseParts.length); } /** * Gets the pre release identifier of this version. If this version has no such * identifier, an empty string is returned. * *

* Note: This method will always reconstruct a new String by joining the single * identifier parts. * * * @return This version's pre release identifier or an empty String if this version * has no such identifier. */ public String getPreRelease() { return join(this.preReleaseParts); } /** * Gets this version's build meta data. If this version has no build meta data, the * returned string is empty. * *

* Note: This method will always reconstruct a new String by joining the single * identifier parts. * * * @return The build meta data or an empty String if this version has no build meta * data. */ public String getBuildMetaData() { return join(this.buildMetaDataParts); } private static String join(String[] parts) { if (parts.length == 0) { return ""; } final StringBuilder b = new StringBuilder(); for (int i = 0; i < parts.length; i++) { final String part = parts[i]; b.append(part); if (i < parts.length - 1) { b.append("."); } } return b.toString(); } /** * Gets the build meta data identifier parts of this version as array. Modifying the * resulting array will have no influence on the internal state of this object. * * @return Build meta data as array. Array is empty if this version has no build meta * data part. */ public String[] getBuildMetaDataParts() { if (this.buildMetaDataParts.length == 0) { return EMPTY_ARRAY; } return Arrays.copyOf(this.buildMetaDataParts, this.buildMetaDataParts.length); } /** * Determines whether this version is still under initial development. * * @return true iff this version's major part is zero. */ public boolean isInitialDevelopment() { return this.major == 0; } /** * Whether this is a 'stable' version. That is, it has no pre-release identifier. * * @return true iff {@link #getPreRelease()} is empty. * @see #isPreRelease() * @since 2.1.0 */ public boolean isStable() { return this.preReleaseParts.length == 0; } /** * Determines whether this is a pre release version. * * @return true iff {@link #getPreRelease()} is not empty. * @see #isStable() */ public boolean isPreRelease() { return this.preReleaseParts.length > 0; } /** * Determines whether this version has a build meta data field. * * @return true iff {@link #getBuildMetaData()} is not empty. */ public boolean hasBuildMetaData() { return this.buildMetaDataParts.length > 0; } /** * Creates a String representation of this version by joining its parts together as by * the semantic version specification. * * @return The version as a String. */ @Override public String toString() { final StringBuilder b = new StringBuilder(TO_STRING_ESTIMATE); b.append(this.major).append(".") .append(this.minor).append(".") .append(this.patch); if (isPreRelease()) { b.append("-").append(getPreRelease()); } if (hasBuildMetaData()) { b.append("+").append(getBuildMetaData()); } return b.toString(); } /** * The hash code for a version instance is computed from the fields {@link #getMajor() * major}, {@link #getMinor() minor}, {@link #getPatch() patch} and * {@link #getPreRelease() pre-release}. * * @return A hash code for this object. */ @Override public int hashCode() { final int h = this.hash; if (h == NOT_YET_CALCULATED) { this.hash = calculateHashCode(); } return this.hash; } private int calculateHashCode() { int h = HASH_PRIME + this.major; h = HASH_PRIME * h + this.minor; h = HASH_PRIME * h + this.patch; h = HASH_PRIME * h + Arrays.hashCode(this.preReleaseParts); return h; } /** * Determines whether this version is equal to the passed object. This is the case if * the passed object is an instance of Version and this version * {@link #compareTo(Version) compared} to the provided one yields 0. Thus, this * method ignores the {@link #getBuildMetaData()} field. * * @param obj the object to compare with. * @return true iff {@code obj} is an instance of {@code Version} and * {@code this.compareTo((Version) obj) == 0} * @see #compareTo(Version) */ @Override public boolean equals(Object obj) { return testEquality(obj, false); } /** * Determines whether this version is equal to the passed object (as determined by * {@link #equals(Object)} and additionally considers the build meta data part of both * versions for equality. * * @param obj The object to compare with. * @return true iff {@code this.equals(obj)} and * {@code this.getBuildMetaData().equals(((Version) obj).getBuildMetaData())} * @since 0.4.0 */ public boolean equalsWithBuildMetaData(Object obj) { return testEquality(obj, true); } private boolean testEquality(Object obj, boolean includeBuildMd) { return obj == this || obj instanceof Version && compare(this, (Version) obj, includeBuildMd) == 0; } /** * Compares this version to the provided one, following the semantic * versioning specification. See {@link #compare(Version, Version)} for more * information. * * @param other The version to compare to. * @return A value lower than 0 if this < other, a value greater than 0 if this * > other and 0 if this == other. The absolute value of the result has no * semantical interpretation. */ @Override public int compareTo(Version other) { return compare(this, other); } /** * Compares this version to the provided one. Unlike the {@link #compareTo(Version)} * method, this one additionally considers the build meta data field of both versions, * if all other parts are equal. Note: This is not part of the semantic * version specification. * *

* Comparison of the build meta data parts happens exactly as for pre release * identifiers. Considering of build meta data first kicks in if both versions are * equal when using their natural order. * * * @param other The version to compare to. * @return A value lower than 0 if this < other, a value greater than 0 if this * > other and 0 if this == other. The absolute value of the result has no * semantical interpretation. * @since 0.3.0 */ public int compareToWithBuildMetaData(Version other) { return compareWithBuildMetaData(this, other); } /** * Returns a new Version where all identifiers are converted to upper case letters. * * @return A new version with lower case identifiers. * @since 1.1.0 */ public Version toUpperCase() { return new Version(this.major, this.minor, this.patch, copyCase(this.preReleaseParts, true), copyCase(this.buildMetaDataParts, true)); } /** * Returns a new Version where all identifiers are converted to lower case letters. * * @return A new version with lower case identifiers. * @since 1.1.0 */ public Version toLowerCase() { return new Version(this.major, this.minor, this.patch, copyCase(this.preReleaseParts, false), copyCase(this.buildMetaDataParts, false)); } private static String[] copyCase(String[] source, boolean toUpper) { final String[] result = new String[source.length]; for (int i = 0; i < source.length; i++) { final String string = source[i]; result[i] = toUpper ? string.toUpperCase(Locale.ROOT) : string.toLowerCase(Locale.ROOT); } return result; } /** * Tests whether this version is strictly greater than the given other version in * terms of precedence. Does not consider the build meta data part. *

* Convenience method for {@code this.compareTo(other) > 0} except that this method * throws an {@link IllegalArgumentException} if other is null. * * * @param other The version to compare to. * @return Whether this version is strictly greater. * @since 1.1.0 */ public boolean isGreaterThan(Version other) { require(other != null, "other must no be null"); return compareTo(other) > 0; } /** * Tests whether this version is equal to or greater than the given other version in * terms of precedence. Does not consider the build meta data part. *

* Convenience method for {@code this.compareTo(other) >= 0} except that this method * throws an {@link IllegalArgumentException} if other is null. * * @param other The version to compare to. * @return Whether this version is greater or equal. * @since 2.1.0 */ public boolean isGreaterThanOrEqualTo(Version other) { require(other != null, "other must no be null"); return compareTo(other) >= 0; } /** * Tests whether this version is strictly lower than the given other version in terms * of precedence. Does not consider the build meta data part. *

* Convenience method for {@code this.compareTo(other) < 0} except that this method * throws an {@link IllegalArgumentException} if other is null. * * @param other The version to compare to. * @return Whether this version is strictly lower. * @since 1.1.0 */ public boolean isLowerThan(Version other) { require(other != null, "other must no be null"); return compareTo(other) < 0; } /** * Tests whether this version is equal to or lower than the given other version in * terms of precedence. Does not consider the build meta data part. *

* Convenience method for {@code this.compareTo(other) <= 0} except that this method * throws an {@link IllegalArgumentException} if other is null. * * @param other The version to compare to. * @return Whether this version is lower or equal. * @since 2.1.0 */ public boolean isLowerThanOrEqualTo(Version other) { require(other != null, "other must no be null"); return compareTo(other) <= 0; } /** * Handles proper deserialization of objects serialized with a version prior to 1.1.0 * * @return the deserialized object. * @throws ObjectStreamException If deserialization fails. * @since 1.1.0 */ private Object readResolve() throws ObjectStreamException { if (this.preRelease != null || this.buildMetaData != null) { return createInternal(this.major, this.minor, this.patch, this.preRelease, this.buildMetaData); } return this; } } libsemantic-version-java-2.1.1+ds/src/main/java/de/skuzzle/semantic/package-info.java000066400000000000000000000001651445177650100305520ustar00rootroot00000000000000/** * Contains a single class semantic version (specification 2.0) implementation. */ package de.skuzzle.semantic; libsemantic-version-java-2.1.1+ds/src/main/java/module-info.java000066400000000000000000000022671445177650100245470ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2017 Simon Taddiken * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ module de.skuzzle.semantic { exports de.skuzzle.semantic; }libsemantic-version-java-2.1.1+ds/src/test/000077500000000000000000000000001445177650100205715ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/test/java/000077500000000000000000000000001445177650100215125ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/test/java/de/000077500000000000000000000000001445177650100221025ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/test/java/de/skuzzle/000077500000000000000000000000001445177650100236115ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/test/java/de/skuzzle/semantic/000077500000000000000000000000001445177650100254145ustar00rootroot00000000000000libsemantic-version-java-2.1.1+ds/src/test/java/de/skuzzle/semantic/CustomGsonSerialization.java000066400000000000000000000042371445177650100331240ustar00rootroot00000000000000package de.skuzzle.semantic; import static org.junit.jupiter.api.Assertions.assertEquals; import java.lang.reflect.Type; import org.junit.jupiter.api.Test; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; public class CustomGsonSerialization { private static class SemanticVersionSerializer implements JsonSerializer, JsonDeserializer { @Override public Version deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { final String versionString = json.getAsString(); return Version.parseVersion(versionString); } @Override public JsonElement serialize(Version src, Type typeOfSrc, JsonSerializationContext context) { return new JsonPrimitive(src.toString()); } } private static final class ObjectWithVersionField { private Version version; private String differentField; public ObjectWithVersionField() { } public ObjectWithVersionField(Version version, String differentField) { this.version = version; this.differentField = differentField; } public Version getVersion() { return this.version; } } @Test public void testCustomGsonSerialization() throws Exception { final Gson gson = new GsonBuilder() .registerTypeAdapter(Version.class, new SemanticVersionSerializer()) .create(); final ObjectWithVersionField object = new ObjectWithVersionField(Version.create(1, 2, 3, "pre-release"), "someString"); final String json = gson.toJson(object); final ObjectWithVersionField object2 = gson.fromJson(json, ObjectWithVersionField.class); assertEquals(object.getVersion(), object2.getVersion()); } } libsemantic-version-java-2.1.1+ds/src/test/java/de/skuzzle/semantic/CustomJacksonSerialization.java000066400000000000000000000052051445177650100336020ustar00rootroot00000000000000package de.skuzzle.semantic; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import org.junit.jupiter.api.Test; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; public class CustomJacksonSerialization { private static class SemanticVersionDeserializer extends JsonDeserializer { @Override public Version deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { final String versionString = p.readValueAs(String.class); return Version.parseVersion(versionString); } } private static final class SemanticVersionSerializer extends JsonSerializer { @Override public void serialize(Version value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeString(value.toString()); } } private static final class ObjectWithVersionField { private Version version; private String differentField; public ObjectWithVersionField() { } public ObjectWithVersionField(Version version, String differentField) { this.version = version; this.differentField = differentField; } public Version getVersion() { return this.version; } public String getDifferentField() { return this.differentField; } } @Test public void testCustomGsonSerialization() throws Exception { final SimpleModule module = new SimpleModule(); module.addDeserializer(Version.class, new SemanticVersionDeserializer()); module.addSerializer(Version.class, new SemanticVersionSerializer()); final ObjectMapper objectMapper = new ObjectMapper().registerModule(module); final ObjectWithVersionField object = new ObjectWithVersionField(Version.create(1, 2, 3, "pre-release"), "someString"); final String json = objectMapper.writeValueAsString(object); final ObjectWithVersionField object2 = objectMapper.readValue(json, ObjectWithVersionField.class); assertEquals(object.getVersion(), object2.getVersion()); } } libsemantic-version-java-2.1.1+ds/src/test/java/de/skuzzle/semantic/IncrementationTest.java000066400000000000000000000143751445177650100321100ustar00rootroot00000000000000package de.skuzzle.semantic; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; public class IncrementationTest { @Test void testToStableIsAlreadyStable() throws Exception { final Version v = Version.create(1, 2, 3).withBuildMetaData("build"); final Version stable = v.toStable(); assertEquals(v, stable); assertFalse(stable.hasBuildMetaData()); } @Test void testToStableDropIdentifiers() throws Exception { final Version v = Version.create(1, 2, 3).withPreRelease("SNAPSHOT").withBuildMetaData("build"); final Version stable = v.toStable(); final Version expected = Version.create(1, 2, 3); assertEquals(expected, stable); assertFalse(stable.hasBuildMetaData()); } @Test public void testIncrementMajor() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertEquals(Version.create(2, 0, 0), v.nextMajor()); } @Test public void testIncrementMajorWithPreReleaseString() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertEquals(Version.create(2, 0, 0, "new.pre.release"), v.nextMajor("new.pre.release")); } @Test public void testIncrementMajorWithPreReleaseArray() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertEquals(Version.create(2, 0, 0, "new.pre.release"), v.nextMajor(new String[] { "new", "pre", "release" })); } @Test public void testIncrementMajorWithPreReleaseStringNull() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertThrows(IllegalArgumentException.class, () -> v.nextMajor((String) null)); } @Test public void testIncrementMajorWithPreReleaseArrayNull() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertThrows(IllegalArgumentException.class, () -> v.nextMajor((String[]) null)); } @Test public void testIncrementMinor() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertEquals(Version.create(1, 3, 0), v.nextMinor()); } @Test public void testIncrementMinorWithPreReleaseString() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertEquals(Version.create(1, 3, 0, "new.pre.release"), v.nextMinor("new.pre.release")); } @Test public void testIncrementMinorWithPreReleaseArray() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertEquals(Version.create(1, 3, 0, "new.pre.release"), v.nextMinor(new String[] { "new", "pre", "release" })); } @Test public void testIncrementMinorWithPreReleaseStringNull() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertThrows(IllegalArgumentException.class, () -> v.nextMinor((String) null)); } @Test public void testIncrementMinorWithPreReleaseArrayNull() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertThrows(IllegalArgumentException.class, () -> v.nextMinor((String[]) null)); } @Test public void testIncrementPatch() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertEquals(Version.create(1, 2, 4), v.nextPatch()); } @Test public void testIncrementPatchWithPreReleaseString() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertEquals(Version.create(1, 2, 4, "new.pre.release"), v.nextPatch("new.pre.release")); } @Test public void testIncrementPatchWithPreReleaseArray() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertEquals(Version.create(1, 2, 4, "new.pre.release"), v.nextPatch(new String[] { "new", "pre", "release" })); } @Test public void testIncrementPatchWithPreReleaseStringNull() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertThrows(IllegalArgumentException.class, () -> v.nextPatch((String) null)); } @Test public void testIncrementPatchWithPreReleaseArrayNull() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertThrows(IllegalArgumentException.class, () -> v.nextPatch((String[]) null)); } @Test public void testIncrementPreReleaseEmpty() throws Exception { final Version v = Version.create(1, 2, 3, "", "build"); assertEquals(Version.create(1, 2, 3, "1"), v.nextPreRelease()); } @Test public void testIncrementPreReleaseNumeric() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release.1", "build"); assertEquals(Version.create(1, 2, 3, "pre-release.2"), v.nextPreRelease()); } @Test public void testIncrementPreReleaseNonNumeric() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertEquals(Version.create(1, 2, 3, "pre-release.1"), v.nextPreRelease()); } @Test public void testIncrementBuildMDEmpty() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", ""); assertEquals(Version.create(1, 2, 3, "pre-release", "1"), v.nextBuildMetaData()); } @Test public void testIncrementBuildMDNumeric() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build.1"); assertEquals(Version.create(1, 2, 3, "pre-release", "build.2"), v.nextBuildMetaData()); } @Test public void testIncrementBuildMDNonNumeric() throws Exception { final Version v = Version.create(1, 2, 3, "pre-release", "build"); assertEquals(Version.create(1, 2, 3, "pre-release", "build.1"), v.nextBuildMetaData()); } } libsemantic-version-java-2.1.1+ds/src/test/java/de/skuzzle/semantic/Java6CompatibilityTest.java000066400000000000000000000040731445177650100326240ustar00rootroot00000000000000package de.skuzzle.semantic; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.stream.Stream; import org.junit.jupiter.api.Test; public class Java6CompatibilityTest { @Test void testAllClassFilesAreCompatibleWithJava6() throws Exception { final int java6 = 0x32; allClassFiles() .filter(cf -> !isModuleInfo(cf)) .forEach(cf -> testClassFileForJavaVersion(cf, java6)); } @Test void testModuleInfoIsCompatibleWithJava9() throws Exception { final int java9 = 0x35; allClassFiles() .filter(this::isModuleInfo) .forEach(mi -> testClassFileForJavaVersion(mi, java9)); } private boolean isModuleInfo(Path path) { return path.getFileName().toString().equals("module-info.class"); } private Stream allClassFiles() throws IOException { final Path targetFolder = Paths.get("./target/classes"); return Files.find(targetFolder, Integer.MAX_VALUE, (path, bfa) -> path.getFileName().toString().endsWith(".class")); } private void testClassFileForJavaVersion(Path path, int expectedClassFileVersion) { final int[] expectedSequence = { 0xCA, 0xFE, 0xBA, 0xBE, 0, 0, 0, expectedClassFileVersion }; try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ)) { for (int i = 0; i < expectedSequence.length; ++i) { final int expected = expectedSequence[i]; final int actual = in.read(); assertEquals(expected, actual, String.format("Class file %s: Expected byte %d at index %d but found %d", path, expected, i, actual)); } } catch (final IOException e) { throw new UncheckedIOException(e); } } } libsemantic-version-java-2.1.1+ds/src/test/java/de/skuzzle/semantic/ParsingTest.java000066400000000000000000000156051445177650100305310ustar00rootroot00000000000000package de.skuzzle.semantic; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.DynamicTest.dynamicTest; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import de.skuzzle.semantic.Version.VersionFormatException; public class ParsingTest { private static final String INCOMPLETE_VERSION_PART = "Incomplete version part in %s"; private static String unexpectedChar(char c) { return "Unexpected char in %s: " + Character.toString(c); } private static final String[] LEGAL_VERSIONS = { "123.456.789", "0.1.0", "0.0.1", "0.0.1-0a", "0.0.1-0a+a0", "1.1.0-a", "1.1.0+a", "1.1.0+012", "1.1.0-112", "1.1.0-0", "1.1.0-0123a", "1.1.0-0123a+0012", }; private static final String[][] ILLEGAL_PRE_RELEASES = { { "01.1", "Illegal leading char '0' in pre-release part of %s" }, { "1.01", "Illegal leading char '0' in pre-release part of %s" }, { "pre.001", "Illegal leading char '0' in pre-release part of %s" }, { "pre.01", "Illegal leading char '0' in pre-release part of %s" }, { "pre..foo", INCOMPLETE_VERSION_PART }, { "$", unexpectedChar('$') } }; private static final String[][] ILLEGAL_VERSIONS = { { "1.", INCOMPLETE_VERSION_PART }, { "1.1.", INCOMPLETE_VERSION_PART }, { "1.0", INCOMPLETE_VERSION_PART }, { "1.2.3-pre.foo.", INCOMPLETE_VERSION_PART }, { "1", INCOMPLETE_VERSION_PART }, { "1$.1.0", unexpectedChar('$') }, { "1.1$.0", unexpectedChar('$') }, { "1.1.1$", unexpectedChar('$') }, { "$.1.1", unexpectedChar('$') }, { "1.$.1", unexpectedChar('$') }, { "1.1.$", unexpectedChar('$') }, { "0$.1.1", unexpectedChar('$') }, { "1.0$.1", unexpectedChar('$') }, { "1.1.0$", unexpectedChar('$') }, { "01.1.0", "Illegal leading char '0' in major part of %s" }, { "1.01.0", "Illegal leading char '0' in minor part of %s" }, { "1.1.01", "Illegal leading char '0' in patch part of %s" }, { "1.2.3-01.1", "Illegal leading char '0' in pre-release part of %s" }, { "1.2.3-1.01+abc", "Illegal leading char '0' in pre-release part of %s" }, { "1.2.3-1.01", "Illegal leading char '0' in pre-release part of %s" }, { "1.2.3-pre.001", "Illegal leading char '0' in pre-release part of %s" }, { "1.2.3-pre.01", "Illegal leading char '0' in pre-release part of %s" }, { "1.2.3-pre.01+a.b", "Illegal leading char '0' in pre-release part of %s" }, { "1.2.3-pre..foo", INCOMPLETE_VERSION_PART }, { "1.2.3+pre..foo", INCOMPLETE_VERSION_PART }, { "1.2.3+pre.foo.", INCOMPLETE_VERSION_PART }, { "1.2.3-$+foo", unexpectedChar('$') }, { "1.2.3+$", unexpectedChar('$') }, { "1.2.3-foo$+foo", unexpectedChar('$') }, { "1.2.3-1$+foo", unexpectedChar('$') }, { "1.2.3-1+1$", unexpectedChar('$') }, { "1.2.3-foo+foo$", unexpectedChar('$') }, { "1.2.3-1+1$", unexpectedChar('$') }, { "1.2.3-0$", unexpectedChar('$') }, { "1.2.3+0$", unexpectedChar('$') }, { "1.2.3-0123$", unexpectedChar('$') }, { "1.2.3-+", unexpectedChar('+') }, }; @TestFactory public Collection testParseWithException() { final List results = new ArrayList<>( ILLEGAL_VERSIONS.length); for (final String[] input : ILLEGAL_VERSIONS) { results.add(dynamicTest("Parse " + input[0], () -> { final VersionFormatException e = assertThrows( VersionFormatException.class, () -> Version.parseVersion(input[0])); final String expectedMessage = String.format(input[1], input[0]); assertEquals(expectedMessage, e.getMessage()); })); } return results; } @TestFactory public Collection testWithPreReleaseException() { final List results = new ArrayList<>( ILLEGAL_PRE_RELEASES.length); for (final String input[] : ILLEGAL_PRE_RELEASES) { results.add(dynamicTest("Pre release " + input[0], () -> { final VersionFormatException e = assertThrows( VersionFormatException.class, () -> Version.create(1, 2, 3).withPreRelease(input[0])); final String expectedMessage = String.format(input[1], input[0]); assertEquals(expectedMessage, e.getMessage()); })); } return results; } @TestFactory public Collection testWithPreReleaseArrayException() { final List results = new ArrayList<>( ILLEGAL_PRE_RELEASES.length); for (final String input[] : ILLEGAL_PRE_RELEASES) { results.add(dynamicTest("Pre release as array: " + input[0], () -> { final VersionFormatException e = assertThrows( VersionFormatException.class, () -> Version.create(1, 2, 3).withPreRelease( input[0].split("\\."))); final String expectedMessage = String.format(input[1], input[0]); assertEquals(expectedMessage, e.getMessage()); })); } return results; } @TestFactory public Collection testParseVerifyOnly() { final List results = new ArrayList<>(); for (final String[] input : ILLEGAL_VERSIONS) { results.add(dynamicTest(input[0], () -> assertFalse(Version.isValidVersion(input[0])))); } return results; } @TestFactory public Collection testParseLegalVersions() { final List results = new ArrayList<>(); for (final String input : LEGAL_VERSIONS) { final DynamicTest test = dynamicTest("Parse " + input, () -> { final Version parsed = Version.parseVersion(input); assertEquals(input, parsed.toString()); }); results.add(test); } return results; } } libsemantic-version-java-2.1.1+ds/src/test/java/de/skuzzle/semantic/VersionTest.java000066400000000000000000001221101445177650100305410ustar00rootroot00000000000000package de.skuzzle.semantic; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.DefaultLocale; import de.skuzzle.semantic.Version.VersionFormatException; public class VersionTest { private static final char[] ILLEGAL_CHAR_BOUNDS = { 'a' - 1, 'z' + 1, '0' - 1, '9' + 1, 'A' - 1, 'Z' + 1, '-' - 1 }; private static final char[] ILLEGAL_NUMERIC_BOUNDS = { '0' - 1, '9' + 1 }; private static final Version[] SEMVER_ORG_VERSIONS = new Version[] { Version.parseVersion("1.0.0-alpha"), Version.parseVersion("1.0.0-alpha.1"), Version.parseVersion("1.0.0-alpha.beta"), Version.parseVersion("1.0.0-beta"), Version.parseVersion("1.0.0-beta.2"), Version.parseVersion("1.0.0-beta.11"), Version.parseVersion("1.0.0-rc.1"), Version.parseVersion("1.0.0"), Version.parseVersion("2.0.0"), Version.parseVersion("2.1.0"), Version.parseVersion("2.1.1") }; // same as above, but exchanged pre release and build meta data private static final Version[] SEMVER_ORG_BMD_VERSIONS = new Version[] { Version.parseVersion("1.0.0-rc.1+alpha"), Version.parseVersion("1.0.0-rc.1+alpha.1"), Version.parseVersion("1.0.0-rc.1+alpha.beta"), Version.parseVersion("1.0.0-rc.1+beta"), Version.parseVersion("1.0.0-rc.1+beta.2"), Version.parseVersion("1.0.0-rc.1+beta.11"), Version.parseVersion("1.0.0-rc.1+rc.1"), Version.parseVersion("1.0.0-rc.1"), Version.parseVersion("2.0.0-rc.1"), Version.parseVersion("2.1.0-rc.1"), Version.parseVersion("2.1.1") }; private static final String[][] ILLEGAL_VERSIONS = { { "1.", "Incomplete version part in 1." }, { "1.1.", "Incomplete version part in 1.1." } }; public static void main(String[] args) throws IOException { new VersionTest().writeBinFile(); } public void writeBinFile() throws IOException { final FileOutputStream out = new FileOutputStream("versions.bin"); final ObjectOutputStream oout = new ObjectOutputStream(out); for (final Version v : SEMVER_ORG_VERSIONS) { oout.writeObject(v); } for (final Version v : SEMVER_ORG_BMD_VERSIONS) { oout.writeObject(v); } oout.close(); } @Test public void testIllegalVersions() throws Exception { for (final String[] input : ILLEGAL_VERSIONS) { try { Version.parseVersion(input[0]); fail("String '" + input[0] + "' should not be parsable"); } catch (final VersionFormatException e) { assertEquals(e.getMessage(), input[1], "Expected different exception message"); } } } @Test public void testPreReleaseEmptyString() { final Version v = Version.create(1, 1, 1, ""); assertEquals("", v.getPreRelease()); assertEquals("", v.getBuildMetaData()); } @Test public void testPreReleaseNull() { assertThrows(IllegalArgumentException.class, () -> Version.create(1, 1, 1, null)); } @Test public void testBuildMDNull() { assertThrows(IllegalArgumentException.class, () -> Version.create(1, 1, 1, "", null)); } @Test public void testNegativePatch() { assertThrows(IllegalArgumentException.class, () -> Version.create(1, 1, -1)); } @Test public void testNegativeMinor() { assertThrows(IllegalArgumentException.class, () -> Version.create(1, -1, 1)); } @Test public void testNegativeMajor() { assertThrows(IllegalArgumentException.class, () -> Version.create(-1, 1, 1)); } @Test public void parseVersionNull() { assertThrows(IllegalArgumentException.class, () -> Version.parseVersion(null)); } @Test public void testSimpleVersion() { final Version v = Version.parseVersion("1.2.3"); assertEquals(1, v.getMajor()); assertEquals(2, v.getMinor()); assertEquals(3, v.getPatch()); assertEquals("", v.getPreRelease()); assertEquals("", v.getBuildMetaData()); } @Test public void testSemVerOrgPreReleaseSamples() { final Version v1 = Version.parseVersion("1.0.0-alpha"); assertEquals("alpha", v1.getPreRelease()); final Version v2 = Version.parseVersion("1.0.0-alpha.1"); assertEquals("alpha.1", v2.getPreRelease()); final Version v3 = Version.parseVersion("1.0.0-0.3.7"); assertEquals("0.3.7", v3.getPreRelease()); final Version v4 = Version.parseVersion("1.0.0-x.7.z.92"); assertEquals("x.7.z.92", v4.getPreRelease()); } @Test public void testSemVerOrgBuildMDSamples() { final Version v1 = Version.parseVersion("1.0.0-alpha+001"); assertEquals("alpha", v1.getPreRelease()); assertEquals("001", v1.getBuildMetaData()); final Version v2 = Version.parseVersion("1.0.0+20130313144700"); assertEquals("20130313144700", v2.getBuildMetaData()); final Version v3 = Version.parseVersion("1.0.0-beta+exp.sha.5114f85"); assertEquals("beta", v3.getPreRelease()); assertEquals("exp.sha.5114f85", v3.getBuildMetaData()); } @Test public void testVersionWithBuildMD() { final Version v = Version.parseVersion("1.2.3+some.id.foo"); assertEquals("some.id.foo", v.getBuildMetaData()); } @Test public void testVersionWithBuildMD2() { final Version v = Version.create(1, 2, 3, "", "some.id-1.foo"); assertEquals("some.id-1.foo", v.getBuildMetaData()); } @Test public void testParseMajorIsZero() throws Exception { final Version version = Version.parseVersion("0.1.2"); assertEquals(0, version.getMajor()); } @Test public void testVersionWithBuildMDEmptyLastPart() { assertThrows(VersionFormatException.class, () -> Version.create(1, 2, 3, "", "some.id.")); } @Test public void testVersionWithBuildMDEmptyMiddlePart() { assertThrows(VersionFormatException.class, () -> Version.create(1, 2, 3, "", "some..id")); } @Test public void testParseBuildMDWithLeadingZeroInIdentifierPart() throws Exception { final Version v = Version.parseVersion("1.2.3+0abc"); assertEquals("0abc", v.getBuildMetaData()); } @Test public void testParsePreReleaseLastPartIsNumeric() throws Exception { final Version v = Version.parseVersion("1.2.3-a.11+buildmd"); assertEquals("a.11", v.getPreRelease()); assertEquals("buildmd", v.getBuildMetaData()); } @Test public void testVersionWithPreRelease() { final Version v = Version.parseVersion("1.2.3-pre.release-foo.1"); assertEquals("pre.release-foo.1", v.getPreRelease()); final String[] expected = { "pre", "release-foo", "1" }; assertArrayEquals(expected, v.getPreReleaseParts()); } @Test public void testVersionWithPreReleaseAndBuildMD() { final Version v = Version .parseVersion("1.2.3-pre.release-foo.1+some.id-with-hyphen"); assertEquals("pre.release-foo.1", v.getPreRelease()); assertEquals("some.id-with-hyphen", v.getBuildMetaData()); } @Test public void testIsValidVersionLeadingZeroMinor() throws Exception { assertFalse(Version.isValidVersion("1.01.1")); } private void shouldNotBeParseable(String template, char c) { final String v = String.format(template, c); try { Version.parseVersion(v); fail("Version " + v + " should not be parsable"); } catch (final VersionFormatException e) { } } @Test public void testIllegalCharNumericParts() throws Exception { for (final char c : ILLEGAL_NUMERIC_BOUNDS) { shouldNotBeParseable("%c.2.3", c); shouldNotBeParseable("1.%c.3", c); shouldNotBeParseable("1.2.%c", c); } } @Test public void testPreReleaseInvalidChar1() throws Exception { for (final char c : ILLEGAL_CHAR_BOUNDS) { shouldNotBeParseable("1.0.0-%c", c); } } @Test public void testPreReleaseInvalidChar2() throws Exception { for (final char c : ILLEGAL_CHAR_BOUNDS) { shouldNotBeParseable("1.0.0-1.a%c", c); } } @Test public void testPreReleaseInvalidChar31() throws Exception { for (final char c : ILLEGAL_CHAR_BOUNDS) { shouldNotBeParseable("1.0.0-01a%c", c); } } @Test public void testBuildMDInvalidChar1() throws Exception { for (final char c : ILLEGAL_CHAR_BOUNDS) { shouldNotBeParseable("1.0.0+%c", c); } } @Test public void testBuildMDInvalidChar2() throws Exception { for (final char c : ILLEGAL_CHAR_BOUNDS) { shouldNotBeParseable("1.0.0+1.a%c", c); } } @Test public void testBuildMetaDataHyphenOnly() throws Exception { final Version v = Version.parseVersion("1.2.3+-"); assertEquals("-", v.getBuildMetaData()); } @Test public void testPreReleaseHyphenOnly() throws Exception { final Version v = Version.parseVersion("1.2.3--"); assertEquals("-", v.getPreRelease()); } @Test public void testParseMajorUnexpectedChar() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1$.0.0")); } @Test public void testParsePatchUnexpectedChar() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.0.1$")); } @Test public void testParseLeadingZeroMinor() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.01.1")); } @Test public void testIsValidVersionLeadingZeroPatch() throws Exception { assertFalse(Version.isValidVersion("1.1.01")); } @Test public void testParseLeadingZeroPatch() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.1.01")); } @Test public void testIsValidVersionLeadingZeroMajor() throws Exception { assertFalse(Version.isValidVersion("01.1.1")); } @Test public void testParseMissingPart() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.0")); } @Test public void testIsValidVersionMissingPart() throws Exception { assertFalse(Version.isValidVersion("1.1")); } @Test public void testParsePrematureStop() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.")); } @Test public void testIsValidVersionPrematureStop() throws Exception { assertFalse(Version.isValidVersion("1.")); } @Test public void testParseMajorLeadingZero() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("01.0.0")); } @Test public void testPreReleaseWithLeadingZeroes() { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-pre.001")); } @Test public void testPreReleaseWithLeadingZeroes2() { assertThrows(VersionFormatException.class, () -> Version.create(1, 2, 3, "pre.001")); } @Test public void testPreReleaseWithLeadingZeroEOS() { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-pre.01")); } @Test public void testPreReleaseWithLeadingZeroEOS2() { assertThrows(VersionFormatException.class, () -> Version.create(1, 2, 3, "pre.01")); } @Test public void testPreReleaseWithLeadingZeroAndBuildMD() { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-pre.01+a.b")); } @Test public void testPreReleaseMiddleEmptyIdentifier() { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-pre..foo")); } @Test public void testPreReleaseLastEmptyIdentifier() { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-pre.foo.")); } @Test public void testBuildMDMiddleEmptyIdentifier() { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3+pre..foo")); } @Test public void testBuildMDLastEmptyIdentifier() { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3+pre.foo.")); } @Test public void testParseExpectNoPrelease() { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-foo", false)); } @Test public void testParseExpectNoBuildMetaData() { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3+foo", false)); } @Test public void testParseExpectNoPreReleaseAndBuildMetaData() { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-foo+foo", false)); } @Test public void testParseVersionIllegalCharInPreReleaseOnly() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-$+foo")); } @Test public void testParseVersionIllegalCharBuildMDOnly() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3+$")); } @Test public void testParseVersionIllegalCharInPreRelease() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-foo$+foo")); } @Test public void testParseVersionIllegalCharInPreReleaseNumericPart() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-1$+foo")); } @Test public void testParseVersionIllegalCharInBuildMDNumericPart() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-1+1$")); } @Test public void testParseVersionIllegalCharInBuildMD() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-foo+foo$")); } @Test public void testParseVersionPreReleaseSingleZero() throws Exception { Version.parseVersion("1.2.3-0.1.0"); } @Test public void testParseVersionPreReleaseAndBuildMDSingleZero() throws Exception { Version.parseVersion("1.2.3-0.1.0+0.0.0.1"); } @Test public void testParsePreReleaseIllegalLeadingZero() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-01.1")); } @Test public void testParsePreReleaseIllegalLeadingZeroBeforeBuildMD() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-1.01+abc")); } @Test public void testParsePreReleaseIllegalLeadingZeroInLastPart() throws Exception { assertThrows(VersionFormatException.class, () -> Version.parseVersion("1.2.3-1.01")); } @Test public void testParseVersionSuccessExpectNoPreRelease() { Version.parseVersion("1.2.3", false); } @Test public void testParseVersionSuccess() { final Version version = Version.parseVersion("1.2.3-foo+bar", true); assertEquals("foo", version.getPreRelease()); assertEquals("bar", version.getBuildMetaData()); } @Test public void testPreReleaseLastEmptyIdentifier2() { assertThrows(VersionFormatException.class, () -> Version.create(1, 2, 3, "pre.foo.")); } @Test public void testVersionAll0() { assertDoesNotThrow(() -> Version.parseVersion("0.0.0")); } @Test public void testVersionAll02() { assertDoesNotThrow(() -> Version.create(0, 0, 0)); } @Test public void testVersionAll03() { assertDoesNotThrow(() -> Version.create(0, 0)); } @Test public void testVersionAll04() { assertDoesNotThrow(() -> Version.create(0)); } @Test public void testPreReleaseInvalid() { assertThrows(VersionFormatException.class, () -> Version.create(1, 2, 3, "pre.", "build")); } @Test public void testPreReleaseNullAndBuildMDGiven() { assertThrows(IllegalArgumentException.class, () -> Version.create(1, 2, 3, null, "build")); } @Test public void testOnlyBuildMdEmpty() { Version.create(1, 2, 3, "pre", ""); } @Test public void testPreReleaseWithLeadingZeroesIdentifier() { // leading zeroes allowed in string identifiers final Version v = Version.parseVersion("1.2.3-001abc"); assertEquals("001abc", v.getPreRelease()); } @Test public void testPreReleaseWithLeadingZeroesIdentifier2() { // leading zeroes allowed in string identifiers final Version v = Version.create(1, 2, 3, "001abc"); assertEquals("001abc", v.getPreRelease()); } @Test public void testNoPrecedenceChangeByBuildMD() { final Version v1 = Version.parseVersion("1.2.3+1.0"); final Version v2 = Version.parseVersion("1.2.3+2.0"); assertEquals(0, v1.compareTo(v2)); } @Test public void testSimplePrecedence() { final Version v1 = Version.parseVersion("1.0.0"); final Version v2 = Version.parseVersion("1.0.1"); final Version v3 = Version.parseVersion("1.1.0"); final Version v4 = Version.parseVersion("2.0.0"); assertTrue(v1.compareTo(v2) < 0); assertTrue(v2.compareTo(v3) < 0); assertTrue(v3.compareTo(v4) < 0); assertTrue(v2.compareTo(v1) > 0); assertTrue(v3.compareTo(v2) > 0); assertTrue(v4.compareTo(v3) > 0); assertTrue(v4.isGreaterThanOrEqualTo(v3)); assertTrue(v3.isLowerThanOrEqualTo(v4)); } @Test public void testPrecedencePreRelease() { final Version v1 = Version.parseVersion("1.0.0"); final Version v2 = Version.parseVersion("1.0.0-rc1"); assertTrue(v1.compareTo(v2) > 0); assertTrue(v2.compareTo(v1) < 0); assertTrue(v2.isLowerThan(v1)); assertTrue(v1.isGreaterThan(v2)); assertTrue(v1.isGreaterThanOrEqualTo(v2)); assertTrue(v2.isLowerThanOrEqualTo(v1)); } @Test public void testPrecedencePreRelease2() { final Version v1 = Version.parseVersion("1.0.0-rc1"); final Version v2 = Version.parseVersion("1.0.0-rc1"); assertTrue(v1.compareTo(v2) == 0); } @Test public void testPrecedencePreRelease3() { final Version v1 = Version.parseVersion("1.0.0-rc1"); final Version v2 = Version.parseVersion("1.0.0-rc1.5"); // the one with longer list is greater assertTrue(v1.compareTo(v2) < 0); assertTrue(v2.compareTo(v1) > 0); } @Test public void testPrecedencePreRelease4() { final Version v1 = Version.parseVersion("1.0.0-a"); final Version v2 = Version.parseVersion("1.0.0-b"); assertTrue(v1.compareTo(v2) < 0); assertTrue(v2.compareTo(v1) > 0); } @Test public void testPrecedencePreRelease5() { final Version v1 = Version.parseVersion("1.0.0-1"); final Version v2 = Version.parseVersion("1.0.0-2"); assertTrue(v1.compareTo(v2) < 0); assertTrue(v2.compareTo(v1) > 0); } @Test public void testPrecedencePreRelease6() { final Version v1 = Version.parseVersion("1.0.0-1.some.id-with-hyphen.a"); final Version v2 = Version.parseVersion("1.0.0-1.some.id-with-hyphen.b"); assertTrue(v1.compareTo(v2) < 0); assertTrue(v2.compareTo(v1) > 0); } @Test public void testIsGreaterNull() throws Exception { assertThrows(IllegalArgumentException.class, () -> Version.create(1, 0, 0).isGreaterThan(null)); } @Test public void testIsLowerNull() throws Exception { assertThrows(IllegalArgumentException.class, () -> Version.create(1, 0, 0).isLowerThan(null)); } @Test public void testIsGreaterThanOrEqualToNull() throws Exception { assertThrows(IllegalArgumentException.class, () -> Version.create(1, 0, 0).isGreaterThanOrEqualTo(null)); } @Test public void testIsLowerThanOrEqualToNull() throws Exception { assertThrows(IllegalArgumentException.class, () -> Version.create(1, 0, 0).isLowerThanOrEqualTo(null)); } @Test public void testInitialDevelopment() { final Version v1 = Version.create(0, 1, 0); final Version v2 = Version.create(1, 1, 0); assertTrue(v1.isInitialDevelopment()); assertFalse(v2.isInitialDevelopment()); } @Test public void testSemVerOrgPrecedenceSample() { for (int i = 1; i < SEMVER_ORG_VERSIONS.length; ++i) { final Version v1 = SEMVER_ORG_VERSIONS[i - 1]; final Version v2 = SEMVER_ORG_VERSIONS[i]; final int c = v1.compareTo(v2); assertTrue(c < 0, v1 + " is not lower than " + v2); assertTrue(v1.isLowerThan(v2)); assertTrue(v1.isLowerThanOrEqualTo(v2)); assertFalse(v1.isGreaterThan(v2)); assertFalse(v1.isGreaterThanOrEqualTo(v2)); } } @Test public void testSemVerOrgPrecedenceSampleComparator() { for (int i = 1; i < SEMVER_ORG_VERSIONS.length; ++i) { final Version v1 = SEMVER_ORG_VERSIONS[i - 1]; final Version v2 = SEMVER_ORG_VERSIONS[i]; final int c = Version.NATURAL_ORDER.compare(v1, v2); assertTrue(c < 0, v1 + " is not lower than " + v2); } } @Test public void testBuildMetaDataEquality() { final Version v1 = Version.create(0, 0, 1, "", "some.build-meta.data"); final Version v2 = Version.create(0, 0, 1, "", "some.different.build-meta.data"); assertFalse(v1.equalsWithBuildMetaData(v2)); } @Test public void testBuildMDPrecedence() { for (int i = 1; i < SEMVER_ORG_BMD_VERSIONS.length; ++i) { final Version v1 = SEMVER_ORG_BMD_VERSIONS[i - 1]; final Version v2 = SEMVER_ORG_BMD_VERSIONS[i]; final int c = v1.compareToWithBuildMetaData(v2); assertTrue(c < 0, v1 + " is not lower than " + v2); } } @Test public void testBuildMDPrecedenceComparator() { for (int i = 1; i < SEMVER_ORG_BMD_VERSIONS.length; ++i) { final Version v1 = SEMVER_ORG_BMD_VERSIONS[i - 1]; final Version v2 = SEMVER_ORG_BMD_VERSIONS[i]; final int c = Version.WITH_BUILD_META_DATA_ORDER.compare(v1, v2); assertTrue(c < 0, v1 + " is not lower than " + v2); } } @Test public void testBuildMDPrecedenceReverse() { for (int i = 1; i < SEMVER_ORG_BMD_VERSIONS.length; ++i) { final Version v1 = SEMVER_ORG_BMD_VERSIONS[i - 1]; final Version v2 = SEMVER_ORG_BMD_VERSIONS[i]; final int c = v2.compareToWithBuildMetaData(v1); assertTrue(c > 0, v2 + " is not greater than " + v1); } } @Test public void testPreReleaseEquality() throws Exception { for (final Version version : SEMVER_ORG_VERSIONS) { final Version copy = Version.create(version.getMajor(), version.getMinor(), version.getPatch(), version.getPreRelease(), version.getBuildMetaData()); assertEquals(version, copy); assertTrue(version.equalsWithBuildMetaData(copy)); assertTrue(version.compareTo(copy) == 0); assertTrue(version.compareToWithBuildMetaData(copy) == 0); assertEquals(version.hashCode(), copy.hashCode()); } } @Test public void testBuildMDEquality() throws Exception { for (final Version version : SEMVER_ORG_BMD_VERSIONS) { final Version copy = Version.create(version.getMajor(), version.getMinor(), version.getPatch(), version.getPreRelease(), version.getBuildMetaData()); assertEquals(version, copy); assertTrue(version.equalsWithBuildMetaData(copy)); assertTrue(version.compareTo(copy) == 0); assertTrue(version.compareToWithBuildMetaData(copy) == 0); assertEquals(version.hashCode(), copy.hashCode()); } } @Test public void testCompareWithBuildMDNull1() throws Exception { assertThrows(NullPointerException.class, () -> Version.compareWithBuildMetaData(null, Version.create(1, 0, 0))); } @Test public void testCompareWithBuildMDNull2() throws Exception { assertThrows(NullPointerException.class, () -> Version.compareWithBuildMetaData(Version.create(1, 0, 0), null)); } @Test public void testCompareNull1() { assertThrows(NullPointerException.class, () -> Version.compare(null, Version.create(1, 1, 1))); } @Test public void testCompareNull2() { assertThrows(NullPointerException.class, () -> Version.compare(Version.create(1, 1, 1), null)); } @Test public void testCompareIdentical() { final Version v = Version.create(1, 1, 1); assertEquals(0, Version.compare(v, v)); } @Test public void testNotEqualsNull() { final Version v = Version.create(1, 1, 1); assertFalse(v.equals(null)); } @Test public void testNotEqualsForeign() { final Version v = Version.create(1, 1, 1); assertFalse(v.equals(new Object())); } @Test public void testEqualsIdentity() { final Version v = Version.create(1, 2, 3); assertEquals(v, v); } @Test public void testNotEqualsTrivial() { final Version v1 = Version.create(1, 1, 1); final Version v2 = Version.create(1, 1, 2); assertFalse(v1.equals(v2)); } @Test public void testParseToString() { for (final Version v1 : SEMVER_ORG_VERSIONS) { final Version v2 = Version.parseVersion(v1.toString()); assertEquals(v1, v2); assertEquals(v1.hashCode(), v2.hashCode()); } } @Test public void testParseToStringUpperCase() { for (final Version v1 : SEMVER_ORG_VERSIONS) { final Version v2 = Version.parseVersion(v1.toString().toUpperCase()); assertEquals(v1.toUpperCase(), v2); assertEquals(v1.toUpperCase().hashCode(), v2.hashCode()); } } @Test public void testParseToStringLowerCase() { for (final Version v1 : SEMVER_ORG_VERSIONS) { final Version v2 = Version.parseVersion(v1.toString().toLowerCase()); assertEquals(v1.toLowerCase(), v2); assertEquals(v1.toLowerCase().hashCode(), v2.hashCode()); } } @Test public void testMin() throws Exception { final Version v1 = Version.create(1, 0, 0); final Version v2 = Version.create(0, 1, 0); assertSame(Version.min(v1, v2), Version.min(v2, v1)); assertSame(v2, Version.min(v1, v2)); assertSame(v2, v2.min(v1)); } @Test public void testMinEquals() throws Exception { final Version v1 = Version.create(1, 0, 0); final Version v2 = Version.create(1, 0, 0); final Version min = Version.min(v1, v2); assertSame(v1, min); assertSame(v1, v1.min(v2)); } @Test public void testMinNullV1() throws Exception { assertThrows(IllegalArgumentException.class, () -> Version.min(null, Version.create(1, 0, 0))); } @Test public void testMinNullV2() throws Exception { assertThrows(IllegalArgumentException.class, () -> Version.min(Version.create(1, 0, 0), null)); } @Test public void testMax() throws Exception { final Version v1 = Version.create(1, 0, 0); final Version v2 = Version.create(0, 1, 0); assertSame(Version.max(v1, v2), Version.max(v2, v1)); assertSame(v1, Version.max(v1, v2)); assertSame(v1, v1.max(v2)); } @Test public void testMaxEquals() throws Exception { final Version v1 = Version.create(1, 0, 0); final Version v2 = Version.create(1, 0, 0); final Version max = Version.max(v1, v2); assertSame(v1, max); assertSame(v1, v1.max(v2)); } @Test public void testMaxNullV1() throws Exception { assertThrows(IllegalArgumentException.class, () -> Version.max(null, Version.create(1, 0, 0))); } @Test public void testMaxNullV2() throws Exception { assertThrows(IllegalArgumentException.class, () -> Version.max(Version.create(1, 0, 0), null)); } @Test public void testSamePrereleaseAndWithBuildMD() throws Exception { final Version v1 = Version.parseVersion("1.0.0-a.b+a"); final Version v2 = Version.parseVersion("1.0.0-a.b+b"); assertTrue(v1.compareToWithBuildMetaData(v2) < 0); } @Test public void testIsNoPreReleaseIdentifierNull() throws Exception { assertFalse(Version.isValidPreRelease(null)); } @Test public void testIsPreReleaseIdentifierEmptyString() throws Exception { assertTrue(Version.isValidPreRelease("")); } @Test public void testIsValidPreReleaseIdentifier() throws Exception { for (final Version v : SEMVER_ORG_VERSIONS) { assertTrue(Version.isValidPreRelease(v.getPreRelease()), v.getPreRelease() + " should be a valid identifier"); } assertTrue(Version.isValidPreRelease("-")); } @Test public void testIsNotValidPreReleaseIdentifier() throws Exception { assertFalse(Version.isValidPreRelease("a+b")); } @Test public void testIsNotValidPreReleaseNumericIdentifier() throws Exception { assertFalse(Version.isValidPreRelease("123+b")); } @Test public void testIsNotValidBuildMDIdentifier() throws Exception { assertFalse(Version.isValidBuildMetaData("a+b")); } @Test public void testIsNotValidBuildMDNumericIdentifier() throws Exception { assertFalse(Version.isValidBuildMetaData("123+b")); } @Test public void testIsNoBuildMDIdentifierNull() throws Exception { assertFalse(Version.isValidBuildMetaData(null)); } @Test public void testIsBuildMDIdentifierEmptyString() throws Exception { assertTrue(Version.isValidBuildMetaData("")); } @Test public void testIsValidBuildMDIdentifier() throws Exception { for (final Version v : SEMVER_ORG_BMD_VERSIONS) { assertTrue(Version.isValidBuildMetaData(v.getBuildMetaData()), v.toString()); assertTrue(Version.isValidBuildMetaData(v.getPreRelease()), v.toString()); } assertTrue(Version.isValidBuildMetaData("-")); } @Test public void testNullIsNoVersion() throws Exception { assertFalse(Version.isValidVersion(null)); } @Test public void testEmptyStringIsNoVersion() throws Exception { assertFalse(Version.isValidVersion("")); } @Test public void testIsValidVersion() throws Exception { for (final Version v : SEMVER_ORG_VERSIONS) { assertTrue(Version.isValidVersion(v.toString())); } } @Test public void testSerialize() throws Exception { final ByteArrayOutputStream bout = new ByteArrayOutputStream(); final ObjectOutputStream out = new ObjectOutputStream(bout); for (final Version v : SEMVER_ORG_VERSIONS) { out.writeObject(v); } out.close(); final InputStream bin = new ByteArrayInputStream(bout.toByteArray()); final ObjectInputStream in = new ObjectInputStream(bin); for (final Version v : SEMVER_ORG_VERSIONS) { assertEquals(v, in.readObject()); } in.close(); } @Test public void testDeserialize05() throws Exception { // Deserialize a file which has been written by version 0.6.0 final ClassLoader cl = getClass().getClassLoader(); final InputStream inp = cl.getResourceAsStream("versions_0.6.bin"); final ObjectInputStream oin = new ObjectInputStream(inp); for (final Version v : SEMVER_ORG_VERSIONS) { assertEquals(v, oin.readObject()); } for (final Version v : SEMVER_ORG_BMD_VERSIONS) { assertEquals(v, oin.readObject()); } oin.close(); } @Test public void testEmptyArrayPreRelease() throws Exception { final Version v = Version.parseVersion("1.0.0"); assertEquals(0, v.getPreReleaseParts().length); } @Test public void testEmptyArrayBuildMetaData() throws Exception { final Version v = Version.parseVersion("1.0.0"); assertEquals(0, v.getBuildMetaDataParts().length); } @Test public void testGetBuildMDParts() throws Exception { final Version v = Version.parseVersion("1.0.0+a.b.c.001"); assertArrayEquals(new String[] { "a", "b", "c", "001" }, v.getBuildMetaDataParts()); } @Test public void testWithMajorAllWillbe0() throws Exception { final Version v = Version.create(1, 0, 0); assertDoesNotThrow(() -> v.withMajor(0)); } @Test public void testWithMinorAllWillbe0() throws Exception { final Version v = Version.create(0, 1, 0); assertDoesNotThrow(() -> v.withMinor(0)); } @Test public void testWithPatchAllWillbe0() throws Exception { final Version v = Version.create(0, 0, 1); assertDoesNotThrow(() -> v.withPatch(0)); } @Test public void testWithMajorKeepEverythingElse() throws Exception { final Version v = Version.create(1, 2, 3, "foo", "bar"); assertEquals(Version.create(2, 2, 3, "foo", "bar"), v.withMajor(2)); } @Test public void testWithMinorKeepEverythingElse() throws Exception { final Version v = Version.create(1, 2, 3, "foo", "bar"); assertEquals(Version.create(1, 1, 3, "foo", "bar"), v.withMinor(1)); } @Test public void testWithPatchKeepEverythingElse() throws Exception { final Version v = Version.create(1, 2, 3, "foo", "bar"); assertEquals(Version.create(1, 2, 2, "foo", "bar"), v.withPatch(2)); } @Test public void testWithPreReleaseKeepEverythingElse() throws Exception { final Version v = Version.create(1, 2, 3, "foo", "bar"); assertEquals(Version.create(1, 2, 3, "bar", "bar"), v.withPreRelease("bar")); } @Test public void testWithPreReleaseEmpty() throws Exception { final Version v = Version.create(1, 2, 3, "foo"); assertEquals(Version.create(1, 2, 3), v.withPreRelease("")); } @Test public void testWithPreReleaseEmptyArray() throws Exception { final Version v = Version.create(1, 2, 3, "foo"); assertEquals(Version.create(1, 2, 3), v.withPreRelease(new String[0])); } @Test public void testWithPreReleaseModifyArray() throws Exception { final String[] newPreRelease = new String[] { "bar" }; final Version v = Version.create(1, 2, 3, "foo"); final Version v2 = v.withPreRelease(newPreRelease); newPreRelease[0] = "foo"; assertEquals(Version.create(1, 2, 3, "bar"), v2); } @Test public void testWithPreReleaseIllegalPart() throws Exception { final String[] newPreRelease = new String[] { "01" }; assertThrows(VersionFormatException.class, () -> Version.create(1, 0, 0).withPreRelease(newPreRelease)); } @Test public void testWithPreReleaseNull() throws Exception { assertThrows(IllegalArgumentException.class, () -> Version.create(1, 2, 3).withPreRelease((String) null)); } @Test public void testWithPreReleaseNull2() throws Exception { assertThrows(IllegalArgumentException.class, () -> Version.create(1, 2, 3).withPreRelease((String[]) null)); } @Test public void testPreReleaseArrayWithDotPart() throws Exception { final Version v = Version.create(1, 0, 0); Assertions.assertEquals( v.withPreRelease(new String[] { "12.a" }), v.withPreRelease(new String[] { "12", "a" })); } @Test public void testBuildMdArrayWithDotPart() throws Exception { assertThrows(VersionFormatException.class, () -> Version.create(1, 0, 0).withPreRelease(new String[] { "12+a" })); } @Test public void testWithBuildMDKeepEverythingElse() throws Exception { final Version v = Version.create(1, 2, 3, "foo", "bar"); assertEquals(Version.create(1, 2, 3, "foo", "foo"), v.withBuildMetaData("foo")); } @Test public void testWithBuildMDEmpty() throws Exception { final Version v = Version.create(1, 2, 3, "", "foo"); assertEquals(Version.create(1, 2, 3), v.withBuildMetaData("")); } @Test public void testWithBuildMDEmptyArray() throws Exception { final Version v = Version.create(1, 2, 3, "", "foo"); assertEquals(Version.create(1, 2, 3), v.withBuildMetaData(new String[0])); } @Test public void testBuildMDArrayWithDotPart() throws Exception { final Version v = Version.create(1, 0, 0); Assertions.assertEquals( v.withBuildMetaData(new String[] { "12.a" }), v.withBuildMetaData(new String[] { "12", "a" })); } @Test public void testWithBuildMDIllegalPartPlus() throws Exception { assertThrows(VersionFormatException.class, () -> Version.create(1, 0, 0).withBuildMetaData(new String[] { "12+a" })); } @Test public void testWithBuildMDModifyArray() throws Exception { final String[] newBuildMd = new String[] { "bar" }; final Version v = Version.create(1, 2, 3, "", "foo"); final Version v2 = v.withBuildMetaData(newBuildMd); newBuildMd[0] = "foo"; assertEquals(Version.create(1, 2, 3, "", "bar"), v2); } @Test public void testWithBuildMdNull() throws Exception { assertThrows(IllegalArgumentException.class, () -> Version.create(1, 2, 3).withBuildMetaData((String) null)); } @Test public void testWithBuildMdNull2() throws Exception { assertThrows(IllegalArgumentException.class, () -> Version.create(1, 2, 3).withBuildMetaData((String[]) null)); } @Test public void testParseBiggerNumbers() throws Exception { final Version v = Version.parseVersion("1234.5678.9012"); assertEquals(1234, v.getMajor()); assertEquals(5678, v.getMinor()); assertEquals(9012, v.getPatch()); } @Test void testIsNotStableWIthPreRelease() throws Exception { assertFalse(Version.create(1, 2, 3).withPreRelease("foo").isStable()); } @Test void testIsStable() throws Exception { assertTrue(Version.create(1, 3, 4).isStable()); } @Test void testIsStableWithBuildMetaData() throws Exception { assertTrue(Version.create(1, 3, 4).withBuildMetaData("foo").isStable()); } // This would have produced an illegal identifier when using toLowerCase with default // locale // https://github.com/skuzzle/semantic-version/pull/5 @Test @DefaultLocale("tr-TR") void testToLowerCaseWithTurkishLocale() throws Exception { final Version lowerCase = Version.create(1, 3, 4).withPreRelease("I") .toLowerCase(); Version.create(1, 3, 4, lowerCase.getPreRelease()); } // This would have produced an illegal identifier when using toLowerCase with default // locale // https://github.com/skuzzle/semantic-version/pull/5 @Test @DefaultLocale("tr-TR") void testToUpperCaseWithTurkishLocale() throws Exception { final Version lowerCase = Version.create(1, 3, 4) .withPreRelease("i") .toUpperCase(); Version.create(1, 3, 4, lowerCase.getPreRelease()); } }