]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5056 Create Duration API
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 5 Mar 2014 15:55:30 +0000 (16:55 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 5 Mar 2014 17:46:30 +0000 (18:46 +0100)
sonar-batch/src/main/java/org/sonar/batch/debt/RuleDebtCalculator.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/Duration.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/utils/DurationTest.java [new file with mode: 0644]

index 2352e7919844d437f5b3fe031b0c590d8d4005c1..547987611912509725309a84d8d7b870f5e05699 100644 (file)
@@ -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 (file)
index 0000000..accff8f
--- /dev/null
@@ -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 (file)
index 0000000..547eeef
--- /dev/null
@@ -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();
+  }
+
+}