mirror of
https://gitee.com/dcren/initializr.git
synced 2026-06-23 00:52:10 +08:00
Support version format
This commit clarifies that Version now handles two different formats, the original one (flagged V1) and a SemVer compliant format (flagged V2). Both Version and VersionRange can switch from one format to the other to produce backward compatible content. See gh-1092
This commit is contained in:
@@ -23,19 +23,25 @@ import java.util.Comparator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.StringJoiner;
|
import java.util.StringJoiner;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the version number of a module. A typical version is represented as
|
* Define a version. A typical version is represented as
|
||||||
* {@code MAJOR.MINOR.PATCH.QUALIFIER} where the qualifier can have an extra version.
|
* {@code MAJOR.MINOR.PATCH[QUALIFIER]} where the qualifier is optional and can have an
|
||||||
|
* extra version.
|
||||||
* <p>
|
* <p>
|
||||||
* For example: {@code 1.2.0.RC1} is the first release candidate of 1.2.0 and
|
* 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}
|
* {@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)
|
* qualifier indicates a final release (a.k.a. GA).
|
||||||
* <p>
|
* <p>
|
||||||
* The main purpose of parsing a version is to compare it with another version, see
|
* Two formats are currently supported, {@link Format#V1} that uses a dot to separate the
|
||||||
* {@link Comparable}.
|
* qualifier from the version itself and {@link Format#V2} that is SemVer compliant (and
|
||||||
|
* therefore uses a dash to separate the qualifier).
|
||||||
|
* <p>
|
||||||
|
* The main purpose of parsing a version is to compare it with another version.
|
||||||
*
|
*
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
*/
|
*/
|
||||||
@@ -54,11 +60,68 @@ public final class Version implements Serializable, Comparable<Version> {
|
|||||||
|
|
||||||
private final Qualifier qualifier;
|
private final Qualifier qualifier;
|
||||||
|
|
||||||
|
private final Format format;
|
||||||
|
|
||||||
public Version(Integer major, Integer minor, Integer patch, Qualifier qualifier) {
|
public Version(Integer major, Integer minor, Integer patch, Qualifier qualifier) {
|
||||||
this.major = major;
|
this.major = major;
|
||||||
this.minor = minor;
|
this.minor = minor;
|
||||||
this.patch = patch;
|
this.patch = patch;
|
||||||
this.qualifier = qualifier;
|
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<String, String> 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() {
|
public Integer getMajor() {
|
||||||
@@ -77,6 +140,10 @@ public final class Version implements Serializable, Comparable<Version> {
|
|||||||
return this.qualifier;
|
return this.qualifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Format getFormat() {
|
||||||
|
return this.format;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the string representation of a {@link Version}. Throws an
|
* Parse the string representation of a {@link Version}. Throws an
|
||||||
* {@link InvalidVersionException} if the version could not be parsed.
|
* {@link InvalidVersionException} if the version could not be parsed.
|
||||||
@@ -262,6 +329,26 @@ public final class Version implements Serializable, Comparable<Version> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<Qualifier> {
|
private static class VersionQualifierComparator implements Comparator<Qualifier> {
|
||||||
|
|
||||||
static final String RELEASE = "RELEASE";
|
static final String RELEASE = "RELEASE";
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package io.spring.initializr.generator.version;
|
package io.spring.initializr.generator.version;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.version.Version.Format;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,9 +29,8 @@ import org.springframework.util.Assert;
|
|||||||
* <ul>
|
* <ul>
|
||||||
* <li>"[1.2.0.RELEASE,1.3.0.RELEASE)" version 1.2.0 and any version after this, up to,
|
* <li>"[1.2.0.RELEASE,1.3.0.RELEASE)" version 1.2.0 and any version after this, up to,
|
||||||
* but not including, version 1.3.0.</li>
|
* but not including, version 1.3.0.</li>
|
||||||
* <li>"(2.0.0.RELEASE,3.2.0.RELEASE]" any version after 2.0.0 up to and including version
|
* <li>"(2.0.0,3.2.0]" any version after 2.0.0 up to and including version 3.2.0.</li>
|
||||||
* 3.2.0.</li>
|
* <li>"2.5.0-M1", the first milestone of 2.5.0 and any version after that.</li>
|
||||||
* <li>"1.4.5.RELEASE", version 1.4.5 and all later versions.</li>
|
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
@@ -83,6 +84,17 @@ public class VersionRange {
|
|||||||
return true;
|
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() {
|
public Version getLowerVersion() {
|
||||||
return this.lowerVersion;
|
return this.lowerVersion;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.version.Version.Format;
|
||||||
import org.assertj.core.api.Condition;
|
import org.assertj.core.api.Condition;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Tests for {@link VersionRange}.
|
||||||
|
*
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
*/
|
*/
|
||||||
class VersionRangeTests {
|
class VersionRangeTests {
|
||||||
@@ -148,6 +151,34 @@ class VersionRangeTests {
|
|||||||
assertThat(range.toRangeString()).isEqualTo("(1.3.5.RELEASE,1.5.5.RELEASE)");
|
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<String> match(String range) {
|
private static Condition<String> match(String range) {
|
||||||
return match(range, new VersionParser(Collections.emptyList()));
|
return match(range, new VersionParser(Collections.emptyList()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,14 @@ import java.util.List;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.version.Version.Format;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Tests for {@link Version}.
|
||||||
|
*
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
*/
|
*/
|
||||||
class VersionTests {
|
class VersionTests {
|
||||||
@@ -153,6 +156,54 @@ class VersionTests {
|
|||||||
"2020.0.0-SNAPSHOT", "2020.0.0");
|
"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) {
|
private Version parse(String text) {
|
||||||
return this.parser.parse(text);
|
return this.parser.parse(text);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user