diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-03-05 16:55:30 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-03-05 18:46:30 +0100 |
commit | 221d3c443d45f024b292cdbe6c0558fcf18f055c (patch) | |
tree | f50d071ad4912bfddf9778434f92596a68de28ed | |
parent | 5db887d91de4e330ac6370a90251050f8ec93517 (diff) | |
download | sonarqube-221d3c443d45f024b292cdbe6c0558fcf18f055c.tar.gz sonarqube-221d3c443d45f024b292cdbe6c0558fcf18f055c.zip |
SONAR-5056 Create Duration API
3 files changed, 303 insertions, 3 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/RuleDebtCalculator.java b/sonar-batch/src/main/java/org/sonar/batch/debt/RuleDebtCalculator.java index 2352e791984..54798761191 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/debt/RuleDebtCalculator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/debt/RuleDebtCalculator.java @@ -38,11 +38,11 @@ import javax.annotation.Nullable; public class RuleDebtCalculator implements BatchExtension { private final TechnicalDebtModel model; - private final int hoursInDay; + private final Settings settings; public RuleDebtCalculator(TechnicalDebtModel model, Settings settings) { this.model = model; - this.hoursInDay = settings.getInt(CoreProperties.HOURS_IN_DAY); + this.settings = settings; } /** @@ -79,7 +79,7 @@ public class RuleDebtCalculator implements BatchExtension { private long convertValueAndUnitToMinutes(int value, WorkDuration.UNIT unit){ if (WorkDuration.UNIT.DAYS.equals(unit)) { - return 60L * value * hoursInDay; + return 60L * value * hoursInDay(); } else if (WorkDuration.UNIT.HOURS.equals(unit)) { return 60L * value; } else if (WorkDuration.UNIT.MINUTES.equals(unit)) { @@ -88,4 +88,8 @@ public class RuleDebtCalculator implements BatchExtension { throw new IllegalStateException("Invalid unit : " + unit); } + private int hoursInDay(){ + return settings.getInt(CoreProperties.HOURS_IN_DAY); + } + } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/Duration.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/Duration.java new file mode 100644 index 00000000000..accff8fc93b --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/Duration.java @@ -0,0 +1,159 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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 java.io.Serializable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @since 4.3 + */ +public class Duration implements Serializable { + + public static final String DAY = "d"; + public static final String HOUR = "h"; + public static final String MINUTE = "min"; + + private static final short HOURS_IN_ONE_DAY = 24; + private static final short MINUTES_IN_ONE_HOUR = 60; + + private final long durationInMinutes; + + private int days; + private int hours; + private int minutes; + + private Duration(long durationInMinutes) { + this.durationInMinutes = durationInMinutes; + this.days = ((Double) ((double) durationInMinutes / HOURS_IN_ONE_DAY / MINUTES_IN_ONE_HOUR)).intValue(); + Long currentDurationInMinutes = durationInMinutes - (days * HOURS_IN_ONE_DAY * MINUTES_IN_ONE_HOUR); + this.hours = ((Double) (currentDurationInMinutes.doubleValue() / MINUTES_IN_ONE_HOUR)).intValue(); + currentDurationInMinutes = currentDurationInMinutes - (hours * MINUTES_IN_ONE_HOUR); + this.minutes = currentDurationInMinutes.intValue(); + } + + private Duration(int days, int hours, int minutes) { + this(calculateDurationInMinutes(days, hours, minutes, HOURS_IN_ONE_DAY)); + } + + public static Duration ofDays(int days) { + return new Duration(days, 0, 0); + } + + public static Duration ofHours(int hours) { + return new Duration(0, hours, 0); + } + + public static Duration ofMinutes(int minutes) { + return new Duration(0, 0, minutes); + } + + public static Duration create(long durationInMinutes) { + return new Duration(durationInMinutes); + } + + public static Duration decode(String text) { + return new Duration(extractValue(text, DAY), extractValue(text, HOUR), extractValue(text, MINUTE)); + } + + private static int extractValue(String text, String unit) { + try { + Pattern pattern = Pattern.compile("(\\d*?)\\D*" + unit); + Matcher matcher = pattern.matcher(text); + if (matcher.find()) { + String daysString = matcher.group(1); + return Integer.parseInt(daysString); + } + return 0; + } catch (NumberFormatException e) { + throw new IllegalArgumentException(String.format("'%s' is invalid, it should use the following sample format : 2d 10h 15min", text), e); + } + } + + public String encode() { + return toString(); + } + + public int days() { + return days; + } + + public int hours() { + return hours; + } + + public int minutes() { + return minutes; + } + + public long toMinutes() { + return durationInMinutes; + } + + public long toMinutes(int hoursInDay) { + return calculateDurationInMinutes(days, hours, minutes, hoursInDay); + } + + private static long calculateDurationInMinutes(int days, int hours, int minutes, int hoursInDay){ + return ((long) days * hoursInDay * MINUTES_IN_ONE_HOUR) + (hours * MINUTES_IN_ONE_HOUR) + minutes; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Duration that = (Duration) o; + if (durationInMinutes != that.durationInMinutes) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return (int) (durationInMinutes ^ (durationInMinutes >>> 32)); + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + if (days > 0) { + stringBuilder.append(days); + stringBuilder.append(DAY); + } + if (hours > 0) { + stringBuilder.append(hours); + stringBuilder.append(HOUR); + } + if (minutes > 0) { + stringBuilder.append(minutes); + stringBuilder.append(MINUTE); + } + return stringBuilder.toString(); + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/DurationTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/DurationTest.java new file mode 100644 index 00000000000..547eeefebbb --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/DurationTest.java @@ -0,0 +1,137 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.Test; + +import static org.fest.assertions.Assertions.assertThat; +import static org.junit.Assert.fail; + +public class DurationTest { + + static final Long ONE_MINUTE = 1L; + static final Long ONE_HOUR_IN_MINUTES = ONE_MINUTE * 60; + static final Long ONE_DAY_IN_MINUTES = ONE_HOUR_IN_MINUTES * 24; + + @Test + public void of_days() throws Exception { + Duration duration = Duration.ofDays(1); + assertThat(duration.days()).isEqualTo(1); + assertThat(duration.hours()).isEqualTo(0); + assertThat(duration.minutes()).isEqualTo(0); + assertThat(duration.toMinutes()).isEqualTo(ONE_DAY_IN_MINUTES); + } + + @Test + public void of_hours() throws Exception { + Duration duration = Duration.ofHours(1); + assertThat(duration.days()).isEqualTo(0); + assertThat(duration.hours()).isEqualTo(1); + assertThat(duration.minutes()).isEqualTo(0); + assertThat(duration.toMinutes()).isEqualTo(ONE_HOUR_IN_MINUTES); + + duration = Duration.ofHours(25); + assertThat(duration.days()).isEqualTo(1); + assertThat(duration.hours()).isEqualTo(1); + assertThat(duration.minutes()).isEqualTo(0); + assertThat(duration.toMinutes()).isEqualTo(ONE_DAY_IN_MINUTES + ONE_HOUR_IN_MINUTES); + } + + @Test + public void of_minutes() throws Exception { + Duration duration = Duration.ofMinutes(1); + assertThat(duration.days()).isEqualTo(0); + assertThat(duration.hours()).isEqualTo(0); + assertThat(duration.minutes()).isEqualTo(1); + assertThat(duration.toMinutes()).isEqualTo(ONE_MINUTE); + + duration = Duration.ofMinutes(61); + assertThat(duration.days()).isEqualTo(0); + assertThat(duration.hours()).isEqualTo(1); + assertThat(duration.minutes()).isEqualTo(1); + assertThat(duration.toMinutes()).isEqualTo(ONE_HOUR_IN_MINUTES + ONE_MINUTE); + } + + @Test + public void create_from_duration_in_minutes() throws Exception { + Duration duration = Duration.create(ONE_DAY_IN_MINUTES + ONE_HOUR_IN_MINUTES + ONE_MINUTE); + assertThat(duration.days()).isEqualTo(1); + assertThat(duration.hours()).isEqualTo(1); + assertThat(duration.minutes()).isEqualTo(1); + assertThat(duration.toMinutes()).isEqualTo(ONE_DAY_IN_MINUTES + ONE_HOUR_IN_MINUTES + ONE_MINUTE); + } + + @Test + public void encode() throws Exception { + assertThat(Duration.create(2 * ONE_DAY_IN_MINUTES + 5 * ONE_HOUR_IN_MINUTES + 46 * ONE_MINUTE).encode()).isEqualTo("2d5h46min"); + assertThat(Duration.create(ONE_DAY_IN_MINUTES).encode()).isEqualTo("1d"); + assertThat(Duration.create(ONE_HOUR_IN_MINUTES).encode()).isEqualTo("1h"); + assertThat(Duration.create(ONE_MINUTE).encode()).isEqualTo("1min"); + } + + @Test + public void decode() throws Exception { + assertThat(Duration.decode(" 15 d 26 h 42min ")).isEqualTo(Duration.create(15 * ONE_DAY_IN_MINUTES + 26 * ONE_HOUR_IN_MINUTES + 42 * ONE_MINUTE)); + assertThat(Duration.decode("26h15d42min")).isEqualTo(Duration.create(15 * ONE_DAY_IN_MINUTES + 26 * ONE_HOUR_IN_MINUTES + 42 * ONE_MINUTE)); + assertThat(Duration.decode("26h")).isEqualTo(Duration.create(26 * ONE_HOUR_IN_MINUTES)); + assertThat(Duration.decode("15d")).isEqualTo(Duration.create(15 * ONE_DAY_IN_MINUTES)); + assertThat(Duration.decode("42min")).isEqualTo(Duration.create(42 * ONE_MINUTE)); + } + + @Test + public void fail_to_decode_if_not_number() throws Exception { + try { + Duration.decode("Xd"); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("'Xd' is invalid, it should use the following sample format : 2d 10h 15min"); + } + } + + @Test + public void convert_to_minutes_with_given_hours_in_day() throws Exception { + assertThat(Duration.create(2 * ONE_DAY_IN_MINUTES).toMinutes(8)).isEqualTo(2 * 8 * ONE_HOUR_IN_MINUTES); + assertThat(Duration.create(2 * ONE_HOUR_IN_MINUTES).toMinutes(8)).isEqualTo(2 * ONE_HOUR_IN_MINUTES); + assertThat(Duration.create(2 * ONE_MINUTE).toMinutes(8)).isEqualTo(2 * ONE_MINUTE); + } + + @Test + public void test_equals_and_hashcode() throws Exception { + Duration duration = Duration.create(ONE_DAY_IN_MINUTES + ONE_HOUR_IN_MINUTES + ONE_MINUTE); + Duration durationWithSameValue = Duration.create(ONE_DAY_IN_MINUTES + ONE_HOUR_IN_MINUTES + ONE_MINUTE); + Duration durationWithDifferentValue = Duration.create(ONE_DAY_IN_MINUTES + ONE_HOUR_IN_MINUTES); + + assertThat(duration).isEqualTo(duration); + assertThat(durationWithSameValue).isEqualTo(duration); + assertThat(durationWithDifferentValue).isNotEqualTo(duration); + assertThat(duration).isNotEqualTo(null); + + assertThat(duration.hashCode()).isEqualTo(duration.hashCode()); + assertThat(durationWithSameValue.hashCode()).isEqualTo(duration.hashCode()); + assertThat(durationWithDifferentValue.hashCode()).isNotEqualTo(duration.hashCode()); + } + + @Test + public void test_toString() throws Exception { + assertThat(Duration.create(ONE_DAY_IN_MINUTES + ONE_HOUR_IN_MINUTES + ONE_MINUTE).toString()).isNotNull(); + } + +} |