From 13f9918a47199fdbd35261e49e6d15b5c8bfdc36 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Tue, 15 Mar 2016 10:14:26 +0100 Subject: [PATCH] SONAR-7458 new component org.sonar.api.utils.Version --- .../java/org/sonar/db/DatabaseChecker.java | 32 +--- .../java/org/sonar/api/utils/Version.java | 161 ++++++++++++++++++ .../java/org/sonar/api/utils/VersionTest.java | 108 ++++++++++++ 3 files changed, 276 insertions(+), 25 deletions(-) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/utils/Version.java create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/utils/VersionTest.java diff --git a/sonar-db/src/main/java/org/sonar/db/DatabaseChecker.java b/sonar-db/src/main/java/org/sonar/db/DatabaseChecker.java index eb436a8a155..d4ce26de573 100644 --- a/sonar-db/src/main/java/org/sonar/db/DatabaseChecker.java +++ b/sonar-db/src/main/java/org/sonar/db/DatabaseChecker.java @@ -27,6 +27,7 @@ import java.util.Map; import org.apache.commons.lang.StringUtils; import org.picocontainer.Startable; import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.Version; import org.sonar.api.utils.log.Loggers; import org.sonar.db.dialect.H2; import org.sonar.db.dialect.MsSql; @@ -44,10 +45,10 @@ public class DatabaseChecker implements Startable { // MsSQL 2012 is 11.x // MsSQL 2014 is 12.x // https://support.microsoft.com/en-us/kb/321185 - MsSql.ID, new Version(10, 0), - MySql.ID, new Version(5, 6), - Oracle.ID, new Version(11, 0), - PostgreSql.ID, new Version(8, 0)); + MsSql.ID, Version.create(10, 0, 0), + MySql.ID, Version.create(5, 6, 0), + Oracle.ID, Version.create(11, 0, 0), + PostgreSql.ID, Version.create(8, 0, 0)); private final Database db; @@ -82,8 +83,8 @@ public class DatabaseChecker implements Startable { try (Connection connection = db.getDataSource().getConnection()) { int dbMajorVersion = connection.getMetaData().getDatabaseMajorVersion(); int dbMinorVersion = connection.getMetaData().getDatabaseMinorVersion(); - Version dbVersion = new Version(dbMajorVersion, dbMinorVersion); - if (!dbVersion.isGreaterThanOrEqual(minDbVersion)) { + Version dbVersion = Version.create(dbMajorVersion, dbMinorVersion, 0); + if (dbVersion.compareTo(minDbVersion) < 0) { throw MessageException.of(String.format( "Unsupported %s version: %s. Minimal supported version is %s.", db.getDialect().getId(), dbVersion, minDbVersion)); } @@ -102,23 +103,4 @@ public class DatabaseChecker implements Startable { } } } - - private static class Version { - private final int major; - private final int minor; - - public Version(int major, int minor) { - this.major = major; - this.minor = minor; - } - - public boolean isGreaterThanOrEqual(Version other) { - return major >= other.major && (major != other.major || minor >= other.minor); - } - - @Override - public String toString() { - return new StringBuilder().append(major).append(".").append(minor).toString(); - } - } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/Version.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/Version.java new file mode 100644 index 00000000000..d1dc9b5dbeb --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/Version.java @@ -0,0 +1,161 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.utils; + +import com.google.common.base.Splitter; +import java.util.List; +import javax.annotation.concurrent.Immutable; + +import static java.lang.Integer.parseInt; +import static org.apache.commons.lang.StringUtils.substringAfter; +import static org.apache.commons.lang.StringUtils.substringBefore; +import static org.apache.commons.lang.StringUtils.trimToEmpty; + +/** + * TODO document equals/comparison without qualifier + * @since 5.5 + */ +@Immutable +public class Version implements Comparable { + + private static final String QUALIFIER_SEPARATOR = "-"; + private static final char SEQUENCE_SEPARATOR = '.'; + private static final Splitter SEQUENCE_SPLITTER = Splitter.on(SEQUENCE_SEPARATOR); + + private final int major; + private final int minor; + private final int patch; + private final String qualifier; + + private Version(int major, int minor, int patch, String qualifier) { + this.major = major; + this.minor = minor; + this.patch = patch; + this.qualifier = qualifier; + } + + public int major() { + return major; + } + + public int minor() { + return minor; + } + + public int patch() { + return patch; + } + + /** + * @return non-null suffix + */ + public String qualifier() { + return qualifier; + } + + public static Version parse(String text) { + String s = trimToEmpty(text); + String qualifier = substringAfter(s, QUALIFIER_SEPARATOR); + if (!qualifier.isEmpty()) { + s = substringBefore(s, QUALIFIER_SEPARATOR); + } + List split = SEQUENCE_SPLITTER.splitToList(s); + int major = 0; + int minor = 0; + int patch = 0; + if (split.size() > 0) { + major = parseSequence(split.get(0)); + if (split.size() > 1) { + minor = parseSequence(split.get(1)); + if (split.size() > 2) { + patch = parseSequence(split.get(2)); + if (split.size() > 3) { + throw new IllegalArgumentException("Only 3 sequences are accepted"); + } + } + } + } + return new Version(major, minor, patch, qualifier); + } + + public static Version create(int major, int minor) { + return new Version(major, minor, 0, ""); + } + + public static Version create(int major, int minor, int patch) { + return new Version(major, minor, patch, ""); + } + + public static Version create(int major, int minor, int patch, String qualifier) { + return new Version(major, minor, patch, qualifier); + } + + private static int parseSequence(String sequence) { + if (sequence.isEmpty()) { + return 0; + } + return parseInt(sequence); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Version)) { + return false; + } + Version other = (Version) o; + return major == other.major && minor == other.minor && patch == other.patch; + } + + @Override + public int hashCode() { + int result = major; + result = 31 * result + minor; + result = 31 * result + patch; + return result; + } + + @Override + public int compareTo(Version other) { + int c = major - other.major; + if (c == 0) { + c = minor - other.minor; + if (c == 0) { + c = patch - other.patch; + } + } + return c; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(major).append(SEQUENCE_SEPARATOR).append(minor); + if (patch > 0) { + sb.append(SEQUENCE_SEPARATOR).append(patch); + } + if (!qualifier.isEmpty()) { + sb.append(QUALIFIER_SEPARATOR).append(qualifier); + } + return sb.toString(); + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/VersionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/VersionTest.java new file mode 100644 index 00000000000..ed765062c20 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/VersionTest.java @@ -0,0 +1,108 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.utils; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.api.utils.Version.parse; + +public class VersionTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void test_parse() { + assertVersion(parse(""), 0, 0, 0, ""); + assertVersion(parse("1"), 1, 0, 0, ""); + assertVersion(parse("1.2"), 1, 2, 0, ""); + assertVersion(parse("1.2.3"), 1, 2, 3, ""); + assertVersion(parse("1.2-beta-1"), 1, 2, 0, "beta-1"); + assertVersion(parse("1.2.3-beta1"), 1, 2, 3, "beta1"); + assertVersion(parse("1.2.3-beta-1"), 1, 2, 3, "beta-1"); + } + + @Test + public void parse_throws_IAE_if_more_than_3_sequences() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Only 3 sequences are accepted"); + parse("1.2.3.4"); + } + + @Test + public void test_equals() { + Version one = parse("1"); + assertThat(one).isEqualTo(one); + assertThat(one).isEqualTo(parse("1")); + assertThat(one).isEqualTo(parse("1.0")); + assertThat(one).isEqualTo(parse("1.0.0")); + assertThat(one).isNotEqualTo(parse("1.2.3")); + + assertThat(parse("1.2.3")).isEqualTo(parse("1.2.3")); + assertThat(parse("1.2.3")).isNotEqualTo(parse("1.2.4")); + assertThat(parse("1.2.3")).isEqualTo(parse("1.2.3-b1")); + assertThat(parse("1.2.3-b1")).isEqualTo(parse("1.2.3-b2")); + } + + @Test + public void test_hashCode() { + assertThat(parse("1").hashCode()).isEqualTo(parse("1").hashCode()); + assertThat(parse("1").hashCode()).isEqualTo(parse("1.0.0").hashCode()); + assertThat(parse("1.2.3-beta1").hashCode()).isEqualTo(parse("1.2.3").hashCode()); + } + + @Test + public void test_compareTo() { + assertThat(parse("1.2").compareTo(parse("1.2.0"))).isEqualTo(0); + assertThat(parse("1.2.3").compareTo(parse("1.2.3"))).isEqualTo(0); + assertThat(parse("1.2.3").compareTo(parse("1.2.4"))).isLessThan(0); + assertThat(parse("1.2.3").compareTo(parse("1.3"))).isLessThan(0); + assertThat(parse("1.2.3").compareTo(parse("2.1"))).isLessThan(0); + assertThat(parse("1.2.3").compareTo(parse("2.0.0"))).isLessThan(0); + assertThat(parse("2.0").compareTo(parse("1.2"))).isGreaterThan(0); + } + + @Test + public void qualifier_is_ignored_from_comparison() { + assertThat(parse("1.2.3")).isEqualTo(parse("1.2.3-build1")); + assertThat(parse("1.2.3")).isEqualTo(parse("1.2.3-build1")); + assertThat(parse("1.2.3").compareTo(parse("1.2.3-build1"))).isEqualTo(0); + } + + @Test + public void test_toString() { + assertThat(parse("1").toString()).isEqualTo("1.0"); + assertThat(parse("1.2").toString()).isEqualTo("1.2"); + assertThat(parse("1.2.3").toString()).isEqualTo("1.2.3"); + assertThat(parse("1.2-b1").toString()).isEqualTo("1.2-b1"); + assertThat(parse("1.2.3-b1").toString()).isEqualTo("1.2.3-b1"); + } + + private static void assertVersion(Version version, + int expectedMajor, int expectedMinor, int expectedPatch, String expectedQualifier) { + assertThat(version.major()).isEqualTo(expectedMajor); + assertThat(version.minor()).isEqualTo(expectedMinor); + assertThat(version.patch()).isEqualTo(expectedPatch); + assertThat(version.qualifier()).isEqualTo(expectedQualifier); + } +} -- 2.39.5