From 0b7614c4f06046b453a01885e347220d913d9f59 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 26 May 2020 11:25:32 +0200 Subject: [PATCH] Add support for SemVer and CalVer versions This commit improves the version parser to handle qualifiers that are separated by either a `.` or a `-`. This makes the parsing of `1.2.0-RC1` or `2020.0.0-M1` possible. Closes gh-1083 --- ...ootVersionRepositoriesBuildCustomizer.java | 4 +- .../initializr/generator/version/Version.java | 95 +++++++++---------- .../generator/version/VersionParser.java | 27 +++--- .../generator/version/VersionParserTests.java | 20 +++- .../generator/version/VersionTests.java | 32 ++++++- 5 files changed, 107 insertions(+), 71 deletions(-) diff --git a/initializr-generator-spring/src/main/java/io/spring/initializr/generator/spring/build/SpringBootVersionRepositoriesBuildCustomizer.java b/initializr-generator-spring/src/main/java/io/spring/initializr/generator/spring/build/SpringBootVersionRepositoriesBuildCustomizer.java index 954a440a..663f90e1 100644 --- a/initializr-generator-spring/src/main/java/io/spring/initializr/generator/spring/build/SpringBootVersionRepositoriesBuildCustomizer.java +++ b/initializr-generator-spring/src/main/java/io/spring/initializr/generator/spring/build/SpringBootVersionRepositoriesBuildCustomizer.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. @@ -44,7 +44,7 @@ class SpringBootVersionRepositoriesBuildCustomizer implements BuildCustomizer { @Override public String toString() { - return this.major + "." + this.minor + "." + this.patch + ((this.qualifier != null) - ? "." + this.qualifier.qualifier + ((this.qualifier.version != null) ? this.qualifier.version : "") - : ""); + StringBuilder sb = new StringBuilder().append(this.major).append(".").append(this.minor).append(".") + .append(this.patch); + if (this.qualifier != null) { + sb.append(this.qualifier.getSeparator()).append(this.qualifier.getId()); + if (this.qualifier.getVersion() != null) { + sb.append(this.qualifier.getVersion()); + } + } + return sb.toString(); } /** @@ -200,74 +208,56 @@ public final class Version implements Serializable, Comparable { */ public static class Qualifier implements Serializable { - public Qualifier(String qualifier) { - this.qualifier = qualifier; + private final String id; + + private final Integer version; + + private final String separator; + + public Qualifier(String id) { + this(id, null, "."); } - private String qualifier; - - private Integer version; - - public String getQualifier() { - return this.qualifier; + public Qualifier(String id, Integer version, String separator) { + this.id = id; + this.version = version; + this.separator = separator; } - public void setQualifier(String qualifier) { - this.qualifier = qualifier; + public String getId() { + return this.id; } public Integer getVersion() { return this.version; } - public void setVersion(Integer version) { - this.version = version; + public String getSeparator() { + return this.separator; } @Override - public boolean equals(Object obj) { - if (this == obj) { + public boolean equals(Object o) { + if (this == o) { return true; } - if (obj == null) { + if (o == null || getClass() != o.getClass()) { return false; } - if (getClass() != obj.getClass()) { - return false; - } - Qualifier other = (Qualifier) obj; - if (this.qualifier == null) { - if (other.qualifier != null) { - return false; - } - } - else if (!this.qualifier.equals(other.qualifier)) { - return false; - } - if (this.version == null) { - if (other.version != null) { - return false; - } - } - else if (!this.version.equals(other.version)) { - return false; - } - return true; + Qualifier qualifier = (Qualifier) o; + return this.id.equals(qualifier.id) && Objects.equals(this.version, qualifier.version) + && Objects.equals(this.separator, qualifier.separator); } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((this.qualifier == null) ? 0 : this.qualifier.hashCode()); - result = prime * result + ((this.version == null) ? 0 : this.version.hashCode()); - return result; + return Objects.hash(this.id, this.version, this.separator); } @Override public String toString() { - return "Qualifier [" + ((this.qualifier != null) ? "qualifier=" + this.qualifier + ", " : "") - + ((this.version != null) ? "version=" + this.version : "") + "]"; + return new StringJoiner(", ", Qualifier.class.getSimpleName() + "[", "]").add("id='" + this.id + "'") + .add("version=" + this.version).add("separator='" + this.separator + "'").toString(); } } @@ -275,11 +265,12 @@ public final class Version implements Serializable, Comparable { private static class VersionQualifierComparator implements Comparator { static final String RELEASE = "RELEASE"; - static final String SNAPSHOT = "BUILD-SNAPSHOT"; + static final String BUILD_SNAPSHOT = "BUILD-SNAPSHOT"; + static final String SNAPSHOT = "SNAPSHOT"; static final String MILESTONE = "M"; static final String RC = "RC"; - static final List KNOWN_QUALIFIERS = Arrays.asList(MILESTONE, RC, SNAPSHOT, RELEASE); + static final List KNOWN_QUALIFIERS = Arrays.asList(MILESTONE, RC, BUILD_SNAPSHOT, SNAPSHOT, RELEASE); @Override public int compare(Qualifier o1, Qualifier o2) { @@ -297,12 +288,12 @@ public final class Version implements Serializable, Comparable { } private static int compareQualifier(Qualifier first, Qualifier second) { - int firstIndex = getQualifierIndex(first.qualifier); - int secondIndex = getQualifierIndex(second.qualifier); + int firstIndex = getQualifierIndex(first.getId()); + int secondIndex = getQualifierIndex(second.getId()); // Unknown qualifier, alphabetic ordering if (firstIndex == -1 && secondIndex == -1) { - return first.qualifier.compareTo(second.qualifier); + return first.getId().compareTo(second.getId()); } else { return Integer.compare(firstIndex, secondIndex); diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/version/VersionParser.java b/initializr-generator/src/main/java/io/spring/initializr/generator/version/VersionParser.java index 3567ea25..90149b6d 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/version/VersionParser.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/version/VersionParser.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. @@ -48,7 +48,7 @@ public class VersionParser { public static final VersionParser DEFAULT = new VersionParser(Collections.emptyList()); private static final Pattern VERSION_REGEX = Pattern - .compile("^(\\d+)\\.(\\d+|x)\\.(\\d+|x)(?:\\.([^0-9]+)(\\d+)?)?$"); + .compile("^(\\d+)\\.(\\d+|x)\\.(\\d+|x)(?:([.|-])([^0-9]+)(\\d+)?)?$"); private static final Pattern RANGE_REGEX = Pattern.compile("(\\(|\\[)(.*),(.*)(\\)|\\])"); @@ -71,20 +71,12 @@ public class VersionParser { Matcher matcher = VERSION_REGEX.matcher(text.trim()); if (!matcher.matches()) { throw new InvalidVersionException("Could not determine version based on '" + text + "': version format " - + "is Major.Minor.Patch.Qualifier " + "(e.g. 1.0.5.RELEASE)"); + + "is Major.Minor.Patch and an optional Qualifier " + "(e.g. 1.0.5.RELEASE)"); } Integer major = Integer.valueOf(matcher.group(1)); String minor = matcher.group(2); String patch = matcher.group(3); - Qualifier qualifier = null; - String qualifierId = matcher.group(4); - if (StringUtils.hasText(qualifierId)) { - qualifier = new Version.Qualifier(qualifierId); - String o = matcher.group(5); - if (o != null) { - qualifier.setVersion(Integer.valueOf(o)); - } - } + Qualifier qualifier = parseQualifier(matcher); if ("x".equals(minor) || "x".equals(patch)) { Integer minorInt = ("x".equals(minor) ? null : Integer.parseInt(minor)); Version latest = findLatestVersion(major, minorInt, qualifier); @@ -99,6 +91,17 @@ public class VersionParser { } } + private Qualifier parseQualifier(Matcher matcher) { + String qualifierSeparator = matcher.group(4); + String qualifierId = matcher.group(5); + if (StringUtils.hasText(qualifierSeparator) && StringUtils.hasText(qualifierId)) { + String versionString = matcher.group(6); + return new Qualifier(qualifierId, (versionString != null) ? Integer.valueOf(versionString) : null, + qualifierSeparator); + } + return null; + } + /** * Parse safely the specified string representation of a {@link Version}. *

diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/version/VersionParserTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/version/VersionParserTests.java index ae9344c4..1fd93930 100755 --- a/initializr-generator/src/test/java/io/spring/initializr/generator/version/VersionParserTests.java +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/version/VersionParserTests.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. @@ -35,23 +35,35 @@ class VersionParserTests { private VersionParser parser = new VersionParser(Collections.emptyList()); @Test - void noQualifierString() { + void versionWithNoQualifier() { Version version = this.parser.parse("1.2.0"); assertThat(version.toString()).isEqualTo("1.2.0"); } @Test - void withQualifierString() { + void versionWithQualifierAndDotSeparator() { Version version = this.parser.parse("1.2.0.RELEASE"); assertThat(version.toString()).isEqualTo("1.2.0.RELEASE"); } @Test - void withQualifierAndVersionString() { + void versionWithQualifierAndDashSeparator() { + Version version = this.parser.parse("1.2.0-SNAPSHOT"); + assertThat(version.toString()).isEqualTo("1.2.0-SNAPSHOT"); + } + + @Test + void versionWithQualifierVersionAndDotSeparator() { Version version = this.parser.parse("1.2.0.RC2"); assertThat(version.toString()).isEqualTo("1.2.0.RC2"); } + @Test + void versionWithQualifierVersionAndDashSeparator() { + Version version = this.parser.parse("1.2.0-M3"); + assertThat(version.toString()).isEqualTo("1.2.0-M3"); + } + @Test void parseInvalidVersion() { assertThatExceptionOfType(InvalidVersionException.class).isThrownBy(() -> this.parser.parse("foo")); 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 d7e4b50e..4aef3f48 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 @@ -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. @@ -17,6 +17,9 @@ package io.spring.initializr.generator.version; import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; @@ -123,6 +126,33 @@ class VersionTests { assertThat(parse("1.2.0.BUILD-SNAPSHOT")).isLessThan(parse("1.2.0.RELEASE")); } + @Test + void orderVersionSchemeWithQualifiedVersions() { + List sortedVersions = Stream + .of("2.3.0.BUILD-SNAPSHOT", "2.3.0.RC1", "2.3.0.M2", "2.3.0.M1", "2.3.0.RELEASE", "2.3.0.RC2") + .map(this::parse).sorted().map(Version::toString).collect(Collectors.toList()); + assertThat(sortedVersions).containsExactly("2.3.0.M1", "2.3.0.M2", "2.3.0.RC1", "2.3.0.RC2", + "2.3.0.BUILD-SNAPSHOT", "2.3.0.RELEASE"); + } + + @Test + void orderVersionSchemeWithSemVer() { + List sortedVersions = Stream + .of("2.3.0-SNAPSHOT", "2.3.0-RC1", "2.3.0-M2", "2.3.0-M1", "2.3.0", "2.3.0-RC2").map(this::parse) + .sorted().map(Version::toString).collect(Collectors.toList()); + assertThat(sortedVersions).containsExactly("2.3.0-M1", "2.3.0-M2", "2.3.0-RC1", "2.3.0-RC2", "2.3.0-SNAPSHOT", + "2.3.0"); + } + + @Test + void orderVersionSchemeWithCalVer() { + List sortedVersions = Stream + .of("2020.0.0-SNAPSHOT", "2020.0.0-RC1", "2020.0.0-M2", "2020.0.0-M1", "2020.0.0", "2020.0.0-RC2") + .map(this::parse).sorted().map(Version::toString).collect(Collectors.toList()); + assertThat(sortedVersions).containsExactly("2020.0.0-M1", "2020.0.0-M2", "2020.0.0-RC1", "2020.0.0-RC2", + "2020.0.0-SNAPSHOT", "2020.0.0"); + } + private Version parse(String text) { return this.parser.parse(text); }