diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/version/Version.java b/initializr-generator/src/main/java/io/spring/initializr/generator/version/Version.java index 0a1c47e4..78bc4b57 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/version/Version.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/version/Version.java @@ -23,19 +23,25 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.StringJoiner; +import java.util.function.Function; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * Define the version number of a module. A typical version is represented as - * {@code MAJOR.MINOR.PATCH.QUALIFIER} where the qualifier can have an extra version. + * Define a version. A typical version is represented as + * {@code MAJOR.MINOR.PATCH[QUALIFIER]} where the qualifier is optional and can have an + * extra version. *

* For example: {@code 1.2.0.RC1} is the first release candidate of 1.2.0 and - * {@code 1.5.0.M4} is the fourth milestone of 1.5.0. The special {@code RELEASE} - * qualifier indicates a final release (a.k.a. GA) + * {@code 1.5.0-M4} is the fourth milestone of 1.5.0. The special {@code RELEASE} + * qualifier indicates a final release (a.k.a. GA). *

- * The main purpose of parsing a version is to compare it with another version, see - * {@link Comparable}. + * Two formats are currently supported, {@link Format#V1} that uses a dot to separate the + * qualifier from the version itself and {@link Format#V2} that is SemVer compliant (and + * therefore uses a dash to separate the qualifier). + *

+ * The main purpose of parsing a version is to compare it with another version. * * @author Stephane Nicoll */ @@ -54,11 +60,68 @@ public final class Version implements Serializable, Comparable { private final Qualifier qualifier; + private final Format format; + public Version(Integer major, Integer minor, Integer patch, Qualifier qualifier) { this.major = major; this.minor = minor; this.patch = patch; this.qualifier = qualifier; + this.format = determineFormat(qualifier); + } + + private static Format determineFormat(Qualifier qualifier) { + if (qualifier == null) { + return Format.V2; + } + return (qualifier.getSeparator().equals(".")) ? Format.V1 : Format.V2; + } + + /** + * Format this version to the specified {@link Format}. + * @param format the format to use + * @return a version compliant with the specified format. + */ + public Version format(Format format) { + Assert.notNull(format, () -> "Format must not be null"); + if (this.format == format) { + return this; + } + if (format == Format.V1) { + Qualifier qualifier = formatQualifier(".", this::toV1Qualifier); + return new Version(this.major, this.minor, this.patch, qualifier); + } + Qualifier qualifier = formatQualifier("-", this::toV2Qualifier); + return new Version(this.major, this.minor, this.patch, qualifier); + } + + private Qualifier formatQualifier(String newSeparator, Function idTransformer) { + String originalQualifier = (this.qualifier != null) ? this.qualifier.getId() : null; + String newId = idTransformer.apply(originalQualifier); + if (newId != null) { + return new Qualifier(newId, (this.qualifier != null) ? this.qualifier.getVersion() : null, newSeparator); + } + return null; + } + + private String toV1Qualifier(String id) { + if ("SNAPSHOT".equals(id)) { + return "BUILD-SNAPSHOT"; + } + if (id == null) { + return "RELEASE"; + } + return id; + } + + private String toV2Qualifier(String id) { + if ("BUILD-SNAPSHOT".equals(id)) { + return "SNAPSHOT"; + } + if ("RELEASE".equals(id)) { + return null; + } + return id; } public Integer getMajor() { @@ -77,6 +140,10 @@ public final class Version implements Serializable, Comparable { return this.qualifier; } + public Format getFormat() { + return this.format; + } + /** * Parse the string representation of a {@link Version}. Throws an * {@link InvalidVersionException} if the version could not be parsed. @@ -262,6 +329,26 @@ public final class Version implements Serializable, Comparable { } + /** + * Define the supported version format. + */ + public enum Format { + + /** + * Original version format, i.e. {@code Major.Minor.Patch.Qualifier} using + * {@code BUILD-SNAPSHOT} as the qualifier for snapshots and {@code RELEASE} for + * GAs. + */ + V1, + + /** + * SemVer-compliant format, i.e. {@code Major.Minor.Patch-Qualifier} using + * {@code SNAPSHOT} as the qualifier for snapshots and no qualifier for GAs. + */ + V2; + + } + private static class VersionQualifierComparator implements Comparator { static final String RELEASE = "RELEASE"; diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/version/VersionRange.java b/initializr-generator/src/main/java/io/spring/initializr/generator/version/VersionRange.java index 8b1d11fa..506a475f 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/version/VersionRange.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/version/VersionRange.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package io.spring.initializr.generator.version; +import io.spring.initializr.generator.version.Version.Format; + import org.springframework.util.Assert; /** @@ -27,9 +29,8 @@ import org.springframework.util.Assert; *

* * @author Stephane Nicoll @@ -83,6 +84,17 @@ public class VersionRange { return true; } + /** + * Format this version range to the specified {@link Format}. + * @param format the version format to use + * @return a version range whose boundaries are compliant with the specified format. + */ + public VersionRange format(Format format) { + Version lower = this.lowerVersion.format(format); + Version higher = (this.higherVersion != null) ? this.higherVersion.format(format) : null; + return new VersionRange(lower, this.lowerInclusive, higher, this.higherInclusive); + } + public Version getLowerVersion() { return this.lowerVersion; } diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/version/VersionRangeTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/version/VersionRangeTests.java index 1d68de48..ccb06861 100755 --- a/initializr-generator/src/test/java/io/spring/initializr/generator/version/VersionRangeTests.java +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/version/VersionRangeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,12 +19,15 @@ package io.spring.initializr.generator.version; import java.util.Arrays; import java.util.Collections; +import io.spring.initializr.generator.version.Version.Format; import org.assertj.core.api.Condition; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; /** + * Tests for {@link VersionRange}. + * * @author Stephane Nicoll */ class VersionRangeTests { @@ -148,6 +151,34 @@ class VersionRangeTests { assertThat(range.toRangeString()).isEqualTo("(1.3.5.RELEASE,1.5.5.RELEASE)"); } + @Test + void formatLowerOnlyV1toV2() { + VersionRange range = parse("1.2.0.RELEASE").format(Format.V2); + assertThat(range.toRangeString()).isEqualTo("1.2.0"); + } + + @Test + void formatV1toV2() { + VersionRange range = parse("[1.2.0.RELEASE,1.3.0.M1)").format(Format.V2); + assertThat(range.toRangeString()).isEqualTo("[1.2.0,1.3.0-M1)"); + } + + @Test + void formatLowerOnlyV2toV1() { + VersionRange range = parse("1.2.0").format(Format.V1); + assertThat(range.toRangeString()).isEqualTo("1.2.0.RELEASE"); + } + + @Test + void formatV2toV1() { + VersionRange range = parse("[1.2.0,1.3.0-M1)").format(Format.V1); + assertThat(range.toRangeString()).isEqualTo("[1.2.0.RELEASE,1.3.0.M1)"); + } + + private static VersionRange parse(String text) { + return new VersionParser(Collections.emptyList()).parseRange(text); + } + private static Condition match(String range) { return match(range, new VersionParser(Collections.emptyList())); } diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/version/VersionTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/version/VersionTests.java index 4aef3f48..9366482f 100755 --- a/initializr-generator/src/test/java/io/spring/initializr/generator/version/VersionTests.java +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/version/VersionTests.java @@ -21,11 +21,14 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import io.spring.initializr.generator.version.Version.Format; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; /** + * Tests for {@link Version}. + * * @author Stephane Nicoll */ class VersionTests { @@ -153,6 +156,54 @@ class VersionTests { "2020.0.0-SNAPSHOT", "2020.0.0"); } + @Test + void formatV1toV1() { + Version version = Version.parse("1.2.0.RELEASE"); + assertThat(version.format(Format.V1)).isSameAs(version); + } + + @Test + void formatV1SnapshotToV2() { + Version version = Version.parse("1.2.0.BUILD-SNAPSHOT"); + assertThat(version.format(Format.V2)).hasToString("1.2.0-SNAPSHOT"); + } + + @Test + void formatV1GAToV2() { + Version version = Version.parse("1.2.0.RELEASE"); + assertThat(version.format(Format.V2)).hasToString("1.2.0"); + } + + @Test + void formatNoQualifierToV1() { + Version version = Version.parse("1.2.0"); + assertThat(version.format(Format.V1)).hasToString("1.2.0.RELEASE"); + } + + @Test + void formatV2toV2() { + Version version = Version.parse("1.2.0-RC1"); + assertThat(version.format(Format.V2)).isSameAs(version); + } + + @Test + void formatV2SnapshotToV1() { + Version version = Version.parse("1.2.0-SNAPSHOT"); + assertThat(version.format(Format.V1)).hasToString("1.2.0.BUILD-SNAPSHOT"); + } + + @Test + void formatV2GAToV1() { + Version version = Version.parse("1.2.0"); + assertThat(version.format(Format.V1)).hasToString("1.2.0.RELEASE"); + } + + @Test + void formatNoQualifierToV2() { + Version version = Version.parse("1.2.0"); + assertThat(version.format(Format.V2)).hasToString("1.2.0"); + } + private Version parse(String text) { return this.parser.parse(text); }