]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5056 Create new Durations API to format Duration and convert String to Duration
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 6 Mar 2014 11:15:15 +0000 (12:15 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 6 Mar 2014 11:15:22 +0000 (12:15 +0100)
37 files changed:
plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/issues.html.erb
plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/technical_debt_pyramid.html.erb
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java
sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java
sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java
sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java
sonar-core/src/main/java/org/sonar/core/i18n/WorkDurationFormatter.java [deleted file]
sonar-core/src/test/java/org/sonar/core/i18n/DefaultI18nTest.java
sonar-core/src/test/java/org/sonar/core/i18n/WorkDurationFormatterTest.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/i18n/I18n.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/Durations.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/utils/internal/WorkDurationFactory.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/utils/DurationsTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/utils/internal/WorkDurationFactoryTest.java [deleted file]
sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java
sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/java/org/sonar/server/technicaldebt/DebtService.java
sonar-server/src/main/java/org/sonar/server/ui/JRubyI18n.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/models/internal.rb
sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb
sonar-server/src/main/webapp/WEB-INF/app/models/project_measure.rb
sonar-server/src/main/webapp/WEB-INF/app/views/issue/_issue.html.erb
sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogFormatterTest.java
sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java
sonar-server/src/test/java/org/sonar/server/technicaldebt/DebtServiceTest.java
sonar-server/src/test/java/org/sonar/server/ui/JRubyI18nTest.java
sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issue.java
sonar-ws-client/src/main/java/org/sonar/wsclient/issue/WorkDayDuration.java [deleted file]
sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultIssue.java
sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultIssueChangeDiff.java
sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultWorkDayDuration.java [deleted file]
sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/IssueJsonParserTest.java
sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/changelog-with-only-new-technical-debt.json
sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/changelog-with-technical-debt.json
sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/issue-with-technical-debt.json

index 660d6236804b6d5bd21c79152b5c5bfdbdb9c3db..a93d324cda2611970d32a9887c00a469b87b14b9 100644 (file)
@@ -71,7 +71,7 @@
                 <br/>
                 <span style="font-weight: bold">
                   <%= message('widget.rules.removed') -%>&nbsp;
-                  <span class="varb"><%= Internal.i18n.formatLongWorkDuration(estimated_cleared_technical_debt) -%></span>
+                  <span class="varb"><%= Internal.i18n.formatLongDuration(estimated_cleared_technical_debt, 'SHORT') -%></span>
                 </span>
               <% end %>
             <% end %>
index 93c3cfd1e2b4fde096be91b6712b6c3bcf40f5ec..a2ecdbd479f5a8e66e6efb4ca5d274b3b6cdf83b 100644 (file)
@@ -1,7 +1,7 @@
 <%
    technical_debt = measure('sqale_index')
 
-   root_characteristics = Internal.technical_debt.findRootCharacteristics().to_a
+   root_characteristics = Internal.debt.findRootCharacteristics().to_a
 
    should_display_diff_measures = dashboard_configuration.selected_period? && technical_debt.variation(dashboard_configuration.period_index)!=nil
    if technical_debt.nil? || root_characteristics.empty?
           </a>
         </td>
         <td class="val value-debt-<%= h(characteristic.key) -%>">
-          <a href="<%= drilldown_url -%>" class="link-debt-<%= h(characteristic.key) -%>"><%= Internal.i18n.formatLongWorkDuration(value.to_i) -%></a>
+          <a href="<%= drilldown_url -%>" class="link-debt-<%= h(characteristic.key) -%>"><%= Internal.i18n.formatLongDuration(value.to_i, 'SHORT') -%></a>
           <% if should_display_diff_measures %>
             <% if diff_by_characteristic_id[characteristic.id] %>
               <%= format_variation(measure) -%>
           <% end %>
         </td>
         <td class="val value-total-<%= h(characteristic.key) -%>">
-          <%= Internal.i18n.formatLongWorkDuration(cumulated.to_i) -%>
+          <%= Internal.i18n.formatLongDuration(cumulated.to_i, 'SHORT') -%>
           <%
              if should_display_diff_measures
                css_style = 'var'
                css_style += 'b' if total_diff < 0
                css_style += 'w' if total_diff > 0
-               diff_to_display = (total_diff < 0 ? '' : '+') + Internal.i18n.formatLongWorkDuration(total_diff.to_i)
+               diff_to_display = (total_diff < 0 ? '' : '+') + Internal.i18n.formatLongDuration(total_diff.to_i, 'SHORT')
           %>
             <span class="<%= css_style -%>"><b>(<%= diff_to_display -%>)</b></span>
           <%
index 88a43090fbde3c83f1d6b3f49de697de85ffa5d3..7428fe50fe67fe27c7cdbff554233982e0cf6440 100644 (file)
@@ -24,15 +24,14 @@ import org.sonar.api.Plugin;
 import org.sonar.api.config.EmailSettings;
 import org.sonar.api.platform.ComponentContainer;
 import org.sonar.api.platform.PluginMetadata;
+import org.sonar.api.utils.Durations;
 import org.sonar.api.utils.HttpDownloader;
 import org.sonar.api.utils.UriReader;
 import org.sonar.api.utils.internal.TempFolderCleaner;
-import org.sonar.api.utils.internal.WorkDurationFactory;
 import org.sonar.batch.components.*;
 import org.sonar.core.config.Logback;
 import org.sonar.core.i18n.DefaultI18n;
 import org.sonar.core.i18n.RuleI18nManager;
-import org.sonar.core.i18n.WorkDurationFormatter;
 import org.sonar.core.metric.CacheMetricFinder;
 import org.sonar.core.persistence.*;
 import org.sonar.core.purge.PurgeProfiler;
@@ -126,8 +125,7 @@ public class BootstrapContainer extends ComponentContainer {
       PastSnapshotFinderByPreviousVersion.class,
       PastMeasuresLoader.class,
       PastSnapshotFinder.class,
-      WorkDurationFormatter.class,
-      WorkDurationFactory.class
+      Durations.class
     );
   }
 
index 016d24a6d25ad80ac5a185028e091be740cf43af..32b6d86fbe2fdd65586fa6e3036b94e584eb532a 100644 (file)
@@ -33,6 +33,7 @@ import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.resources.ResourceUtils;
 import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
 import org.sonar.core.timemachine.Periods;
 
 import java.util.Collection;
@@ -48,13 +49,15 @@ public class QualityGateVerifier implements Decorator {
   private final Snapshot snapshot;
   private final Periods periods;
   private final I18n i18n;
+  private final Durations durations;
   private ProjectAlerts projectAlerts;
 
-  public QualityGateVerifier(Snapshot snapshot, ProjectAlerts projectAlerts, Periods periods, I18n i18n) {
+  public QualityGateVerifier(Snapshot snapshot, ProjectAlerts projectAlerts, Periods periods, I18n i18n, Durations durations) {
     this.snapshot = snapshot;
     this.projectAlerts = projectAlerts;
     this.periods = periods;
     this.i18n = i18n;
+    this.durations = durations;
   }
 
   @DependedUpon
@@ -149,10 +152,10 @@ public class QualityGateVerifier implements Decorator {
     return stringBuilder.toString();
   }
 
-  private String alertValue(Alert alert, Metric.Level level){
+  private String alertValue(Alert alert, Metric.Level level) {
     String value = level.equals(Metric.Level.ERROR) ? alert.getValueError() : alert.getValueWarning();
     if (alert.getMetric().getType().equals(Metric.ValueType.WORK_DUR)) {
-      return i18n.formatWorkDuration(Locale.ENGLISH, Duration.create(Long.parseLong(value)));
+      return durations.format(Locale.ENGLISH, Duration.create(Long.parseLong(value)), Durations.DurationFormat.SHORT);
     } else {
       return value;
     }
index bf1d149df9c8429905c4694110c5e8ccc056f49f..ef894d678519a665220b1eceeb07f1ffc80368de 100644 (file)
@@ -38,6 +38,7 @@ import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.test.IsMeasure;
 import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
 import org.sonar.core.timemachine.Periods;
 
 import java.util.Locale;
@@ -62,12 +63,14 @@ public class QualityGateVerifierTest {
   Snapshot snapshot;
   Periods periods;
   I18n i18n;
+  Durations durations;
 
   @Before
   public void before() {
     context = mock(DecoratorContext.class);
     periods = mock(Periods.class);
     i18n = mock(I18n.class);
+    durations = mock(Durations.class);
     when(i18n.message(any(Locale.class), eq("variation"), eq("variation"))).thenReturn("variation");
 
     measureClasses = new Measure(CoreMetrics.CLASSES, 20d);
@@ -80,7 +83,7 @@ public class QualityGateVerifierTest {
 
     snapshot = mock(Snapshot.class);
     projectAlerts = new ProjectAlerts();
-    verifier = new QualityGateVerifier(snapshot, projectAlerts, periods, i18n);
+    verifier = new QualityGateVerifier(snapshot, projectAlerts, periods, i18n, durations);
     project = new Project("foo");
   }
 
@@ -351,7 +354,7 @@ public class QualityGateVerifierTest {
 
     // metric name is declared in l10n bundle
     when(i18n.message(any(Locale.class), eq("metric.tech_debt.name"), anyString())).thenReturn("The Debt");
-    when(i18n.formatWorkDuration(any(Locale.class), eq(Duration.create(3600L)))).thenReturn("1h");
+    when(durations.format(any(Locale.class), eq(Duration.create(3600L)), eq(Durations.DurationFormat.SHORT))).thenReturn("1h");
 
     when(context.getMeasure(metric)).thenReturn(new Measure(metric, 1800d));
     projectAlerts.addAll(Lists.newArrayList(new Alert(null, metric, Alert.OPERATOR_SMALLER, "3600", null)));
index eebe7325cd71c60ed06734e9d69753dd153e9c9b..23fe352d0a8c1e020d21423af1e04a21b79b07cf 100644 (file)
@@ -30,7 +30,6 @@ import org.sonar.api.ServerExtension;
 import org.sonar.api.i18n.I18n;
 import org.sonar.api.platform.PluginMetadata;
 import org.sonar.api.platform.PluginRepository;
-import org.sonar.api.utils.Duration;
 import org.sonar.api.utils.SonarException;
 import org.sonar.api.utils.System2;
 
@@ -48,7 +47,6 @@ public class DefaultI18n implements I18n, ServerExtension, BatchExtension, Start
   private static final Logger LOG = LoggerFactory.getLogger(DefaultI18n.class);
 
   public static final String BUNDLE_PACKAGE = "org.sonar.l10n.";
-  private final WorkDurationFormatter workDurationFormatter;
 
   private PluginRepository pluginRepository;
   private I18nClassloader i18nClassloader;
@@ -56,14 +54,13 @@ public class DefaultI18n implements I18n, ServerExtension, BatchExtension, Start
   private final ResourceBundle.Control control;
   private final System2 system2;
 
-  public DefaultI18n(PluginRepository pluginRepository, WorkDurationFormatter workDurationFormatter) {
-    this(pluginRepository, workDurationFormatter, System2.INSTANCE);
+  public DefaultI18n(PluginRepository pluginRepository) {
+    this(pluginRepository, System2.INSTANCE);
   }
 
   @VisibleForTesting
-  DefaultI18n(PluginRepository pluginRepository, WorkDurationFormatter workDurationFormatter, System2 system2) {
+  DefaultI18n(PluginRepository pluginRepository, System2 system2) {
     this.pluginRepository = pluginRepository;
-    this.workDurationFormatter = workDurationFormatter;
     this.system2 = system2;
     // SONAR-2927
     this.control = new ResourceBundle.Control() {
@@ -148,24 +145,6 @@ public class DefaultI18n implements I18n, ServerExtension, BatchExtension, Start
     return DateFormat.getDateInstance(DateFormat.DEFAULT, locale).format(date);
   }
 
-  @Override
-  public String formatWorkDuration(Locale locale, Duration duration) {
-    Long durationInMinutes = duration.toMinutes();
-    if (durationInMinutes == 0) {
-      return "0";
-    }
-    List<WorkDurationFormatter.Result> results = workDurationFormatter.format(durationInMinutes);
-    StringBuilder message = new StringBuilder();
-    for (WorkDurationFormatter.Result result : results) {
-      if (" ".equals(result.key())) {
-        message.append(" ");
-      } else {
-        message.append(message(locale, result.key(), null, result.value()));
-      }
-    }
-    return message.toString();
-  }
-
   /**
    * Only the given locale is searched. Contrary to java.util.ResourceBundle, no strategy for locating the bundle is implemented in
    * this method.
diff --git a/sonar-core/src/main/java/org/sonar/core/i18n/WorkDurationFormatter.java b/sonar-core/src/main/java/org/sonar/core/i18n/WorkDurationFormatter.java
deleted file mode 100644 (file)
index f760bf4..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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.core.i18n;
-
-import org.apache.commons.lang.builder.ReflectionToStringBuilder;
-import org.apache.commons.lang.builder.ToStringStyle;
-import org.sonar.api.BatchExtension;
-import org.sonar.api.ServerComponent;
-import org.sonar.api.utils.internal.WorkDuration;
-import org.sonar.api.utils.internal.WorkDurationFactory;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-
-import java.util.List;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-public class WorkDurationFormatter implements ServerComponent, BatchExtension {
-
-  private final WorkDurationFactory workDurationFactory;
-
-  public WorkDurationFormatter(WorkDurationFactory workDurationFactory) {
-    this.workDurationFactory = workDurationFactory;
-  }
-
-  public List<Result> format(long durationInMinutes) {
-    if (durationInMinutes == 0) {
-      return newArrayList(new Result("0", null));
-    }
-    boolean isNegative = durationInMinutes < 0;
-    Long absDuration = Math.abs(durationInMinutes);
-    return format(workDurationFactory.createFromMinutes(absDuration), isNegative);
-  }
-
-  private List<Result> format(WorkDuration workDuration, boolean isNegative) {
-    List<Result> results = newArrayList();
-    if (workDuration.days() > 0) {
-      results.add(message("work_duration.x_days", isNegative ? -1 * workDuration.days() : workDuration.days()));
-    }
-    if (displayHours(workDuration)) {
-      addSpaceIfNeeded(results);
-      results.add(message("work_duration.x_hours", isNegative && results.isEmpty() ? -1 * workDuration.hours() : workDuration.hours()));
-    }
-    if (displayMinutes(workDuration)) {
-      addSpaceIfNeeded(results);
-      results.add(message("work_duration.x_minutes", isNegative && results.isEmpty() ? -1 * workDuration.minutes() : workDuration.minutes()));
-    }
-    return results;
-  }
-
-  private boolean displayHours(WorkDuration workDuration){
-    return workDuration.hours() > 0 && workDuration.days() < 10;
-  }
-
-  private boolean displayMinutes(WorkDuration workDuration){
-    return workDuration.minutes() > 0 && workDuration.hours() < 10 && workDuration.days() == 0;
-  }
-
-  private void addSpaceIfNeeded(List<Result> results){
-    if (!results.isEmpty()) {
-      results.add(new Result(" ", null));
-    }
-  }
-
-  private Result message(String key, @CheckForNull Object parameter) {
-    return new Result(key, parameter);
-  }
-
-  static class Result {
-    private String key;
-    private Object value;
-
-    Result(String key, @Nullable Object value) {
-      this.key = key;
-      this.value = value;
-    }
-
-    String key() {
-      return key;
-    }
-
-    @CheckForNull
-    Object value() {
-      return value;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-      if (this == o) {
-        return true;
-      }
-      if (o == null || getClass() != o.getClass()) {
-        return false;
-      }
-
-      Result result = (Result) o;
-
-      if (key != null ? !key.equals(result.key) : result.key != null) {
-        return false;
-      }
-      if (value != null ? !value.equals(result.value) : result.value != null) {
-        return false;
-      }
-
-      return true;
-    }
-
-    @Override
-    public int hashCode() {
-      int result = key != null ? key.hashCode() : 0;
-      result = 31 * result + (value != null ? value.hashCode() : 0);
-      return result;
-    }
-
-    @Override
-    public String toString() {
-      return new ReflectionToStringBuilder(this, ToStringStyle.SIMPLE_STYLE).toString();
-    }
-  }
-}
index 954f4802092a5704c11c40f14b162e2dbc34aaa7..3c5bc0d6d48510e1780b66322e59372397d5a1ce 100644 (file)
@@ -27,7 +27,6 @@ import org.mockito.runners.MockitoJUnitRunner;
 import org.sonar.api.platform.PluginMetadata;
 import org.sonar.api.platform.PluginRepository;
 import org.sonar.api.utils.DateUtils;
-import org.sonar.api.utils.Duration;
 import org.sonar.api.utils.System2;
 
 import java.net.URL;
@@ -36,7 +35,6 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 
-import static com.google.common.collect.Lists.newArrayList;
 import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -47,10 +45,6 @@ public class DefaultI18nTest {
   @Mock
   System2 system2;
 
-  @Mock
-  WorkDurationFormatter workDurationFormatter;
-
-
   DefaultI18n manager;
 
   @Before
@@ -62,7 +56,7 @@ public class DefaultI18nTest {
     I18nClassloader i18nClassloader = new I18nClassloader(new ClassLoader[]{
       newCoreClassloader(), newFrenchPackClassloader(), newSqaleClassloader(), newCheckstyleClassloader()
     });
-    manager = new DefaultI18n(pluginRepository, workDurationFormatter, system2);
+    manager = new DefaultI18n(pluginRepository, system2);
     manager.doStart(i18nClassloader);
   }
 
@@ -200,23 +194,6 @@ public class DefaultI18nTest {
     assertThat(manager.formatDate(Locale.ENGLISH, DateUtils.parseDate("2014-01-22"))).isEqualTo("Jan 22, 2014");
   }
 
-  @Test
-  public void format_work_duration() {
-    when(workDurationFormatter.format(10)).thenReturn(newArrayList(
-      new WorkDurationFormatter.Result("work_duration.x_days", 5),
-      new WorkDurationFormatter.Result(" ", null),
-      new WorkDurationFormatter.Result("work_duration.x_hours", 2),
-      new WorkDurationFormatter.Result(" ", null),
-      new WorkDurationFormatter.Result("work_duration.x_minutes", 1)
-    ));
-    assertThat(manager.formatWorkDuration(Locale.ENGLISH, Duration.create(10))).isEqualTo("5d 2h 1min");
-  }
-
-  @Test
-  public void format_work_duration_when_0() {
-    assertThat(manager.formatWorkDuration(Locale.ENGLISH, Duration.create(0))).isEqualTo("0");
-  }
-
   static URLClassLoader newCoreClassloader() {
     return newClassLoader("/org/sonar/core/i18n/corePlugin/");
   }
diff --git a/sonar-core/src/test/java/org/sonar/core/i18n/WorkDurationFormatterTest.java b/sonar-core/src/test/java/org/sonar/core/i18n/WorkDurationFormatterTest.java
deleted file mode 100644 (file)
index 984b2f7..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.core.i18n;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Settings;
-import org.sonar.api.utils.internal.WorkDurationFactory;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.fest.assertions.Assertions.assertThat;
-
-public class WorkDurationFormatterTest {
-
-  static final int HOURS_IN_DAY = 8;
-
-  static final long ONE_MINUTE = 1L;
-  static final long ONE_HOUR = ONE_MINUTE * 60;
-  static final long ONE_DAY = HOURS_IN_DAY * ONE_HOUR;
-
-  Settings settings;
-
-  WorkDurationFormatter formatter;
-
-  @Before
-  public void setUp() throws Exception {
-    settings = new Settings();
-    settings.setProperty(CoreProperties.HOURS_IN_DAY, Integer.toString(HOURS_IN_DAY));
-    formatter = new WorkDurationFormatter(new WorkDurationFactory(settings));
-  }
-
-  @Test
-  public void format() {
-    assertThat(formatter.format(5 * ONE_DAY)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_days", 5)));
-    assertThat(formatter.format(2 * ONE_HOUR)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_hours", 2)));
-    assertThat(formatter.format(ONE_MINUTE)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_minutes", 1)));
-
-    assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR)).isEqualTo(newArrayList(
-      new WorkDurationFormatter.Result("work_duration.x_days", 5),
-      new WorkDurationFormatter.Result(" ", null),
-      new WorkDurationFormatter.Result("work_duration.x_hours", 2)
-    ));
-
-    assertThat(formatter.format(2 * ONE_HOUR + ONE_MINUTE)).isEqualTo(newArrayList(
-      new WorkDurationFormatter.Result("work_duration.x_hours", 2),
-      new WorkDurationFormatter.Result(" ", null),
-      new WorkDurationFormatter.Result("work_duration.x_minutes", 1)
-    ));
-
-    assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE)).isEqualTo(newArrayList(
-      new WorkDurationFormatter.Result("work_duration.x_days", 5),
-      new WorkDurationFormatter.Result(" ", null),
-      new WorkDurationFormatter.Result("work_duration.x_hours", 2)
-    ));
-  }
-
-  @Test
-  public void not_display_following_element_when_bigger_than_ten() {
-    int hoursInDay = 15;
-    settings.setProperty(CoreProperties.HOURS_IN_DAY, Integer.toString(hoursInDay));
-
-    assertThat(formatter.format(15 * hoursInDay * ONE_HOUR + 2 * ONE_HOUR + ONE_MINUTE)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_days", 15)));
-
-    assertThat(formatter.format(12 * ONE_HOUR + ONE_MINUTE)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_hours", 12)));
-  }
-
-  @Test
-  public void display_zero_without_unit() {
-    assertThat(formatter.format(0)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("0", null)));
-  }
-
-  @Test
-  public void display_negative_duration() {
-    assertThat(formatter.format(-5 * ONE_DAY)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_days", -5)));
-    assertThat(formatter.format(-2 * ONE_HOUR)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_hours", -2)));
-    assertThat(formatter.format(-1 * ONE_MINUTE)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_minutes", -1)));
-  }
-
-}
index e30990f12dcf019c47010140b805a2622b5eba45..bf8b6ee9a75773457bced44ba09033beba274bf4 100644 (file)
@@ -21,7 +21,6 @@ package org.sonar.api.i18n;
 
 import org.sonar.api.BatchComponent;
 import org.sonar.api.ServerComponent;
-import org.sonar.api.utils.Duration;
 
 import javax.annotation.Nullable;
 
@@ -71,16 +70,16 @@ public interface I18n extends ServerComponent, BatchComponent {
 
   /**
    * Return the distance in time between two dates.
-   * @see I18n#age(java.util.Locale, long durationInMillis)
    *
+   * @see I18n#age(java.util.Locale, long durationInMillis)
    * @since 4.2
    */
   String age(Locale locale, Date fromDate, Date toDate);
 
   /**
    * Reports the distance in time a date and now.
-   * @see I18n#age(java.util.Locale, java.util.Date, java.util.Date)
    *
+   * @see I18n#age(java.util.Locale, java.util.Date, java.util.Date)
    * @since 4.2
    */
   String ageFromNow(Locale locale, Date date);
@@ -103,13 +102,4 @@ public interface I18n extends ServerComponent, BatchComponent {
    */
   String formatDate(Locale locale, Date date);
 
-  /**
-   * Return the formatted work duration.
-   * <br>
-   * Example : format(Locale.ENGLISH, Duration.create(10 * 24 * 60 + 2 * 60)) -> 10d 2h
-   *
-   * @since 4.3
-   */
-  String formatWorkDuration(Locale locale, Duration duration);
-
 }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/Durations.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/Durations.java
new file mode 100644 (file)
index 0000000..7f0f5ef
--- /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.sonar.api.BatchComponent;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.config.Settings;
+import org.sonar.api.i18n.I18n;
+
+import javax.annotation.CheckForNull;
+
+import java.util.Locale;
+
+/**
+ * Used through ruby code <pre>Internal.durations</pre>
+ *
+ * @since 4.3
+ */
+public class Durations implements BatchComponent, ServerComponent {
+
+  public enum DurationFormat {
+    /**
+     * Display duration with only one or two members.
+     * For instance, Duration.decode("1d 1h 10min", 8) will return "1d 1h" and Duration.decode("12d 5h", 8) will return "12d"
+     */
+    SHORT
+  }
+
+  private final Settings settings;
+  private final I18n i18n;
+
+  public Durations(Settings settings, I18n i18n) {
+    this.settings = settings;
+    this.i18n = i18n;
+  }
+
+  /**
+   * Create a Duration object from a number of minutes
+   */
+  public Duration create(long minutes) {
+    return Duration.create(minutes);
+  }
+
+  /**
+   * Convert the text to a Duration
+   * <br>
+   * Example : decode("9d 10 h") -> Duration.encode("10d2h") (if sonar.technicalDebt.hoursInDay property is set to 8)
+   */
+  public Duration decode(String duration) {
+    return Duration.decode(duration, hoursInDay());
+  }
+
+  /**
+   * Return the string value of the Duration.
+   * <br>
+   * Example : encode(Duration.encode("9d 10h")) -> "10d2h" (if sonar.technicalDebt.hoursInDay property is set to 8)
+   */
+  public String encode(Duration duration) {
+    return duration.encode(hoursInDay());
+  }
+
+  /**
+   * Return the formatted work duration.
+   * <br>
+   * Example : format(Locale.FRENCH, Duration.encode("9d 10h"), DurationFormat.SHORT) -> 10j 2h (if sonar.technicalDebt.hoursInDay property is set to 8)
+   */
+  public String format(Locale locale, Duration duration, DurationFormat format) {
+    Long durationInMinutes = duration.toMinutes();
+    if (durationInMinutes == 0) {
+      return "0";
+    }
+    boolean isNegative = durationInMinutes < 0;
+    Long absDuration = Math.abs(durationInMinutes);
+
+    int days = ((Double) ((double) absDuration / hoursInDay() / 60)).intValue();
+    Long remainingDuration = absDuration - (days * hoursInDay() * 60);
+    int hours = ((Double) (remainingDuration.doubleValue() / 60)).intValue();
+    remainingDuration = remainingDuration - (hours * 60);
+    int minutes = remainingDuration.intValue();
+
+    StringBuilder message = new StringBuilder();
+    if (days > 0) {
+      message.append(message(locale, "work_duration.x_days", isNegative ? -1 * days : days));
+    }
+    if (displayHours(days, hours)) {
+      addSpaceIfNeeded(message);
+      message.append(message(locale, "work_duration.x_hours", isNegative && message.length() == 0 ? -1 * hours : hours));
+    }
+    if (displayMinutes(days, hours, minutes)) {
+      addSpaceIfNeeded(message);
+      message.append(message(locale, "work_duration.x_minutes", isNegative && message.length() == 0 ? -1 * minutes : minutes));
+    }
+    return message.toString();
+  }
+
+  private String message(Locale locale, String key, @CheckForNull Object parameter) {
+    return i18n.message(locale, key, null, parameter);
+  }
+
+  private boolean displayHours(int days, int hours) {
+    return hours > 0 && days < 10;
+  }
+
+  private boolean displayMinutes(int days, int hours, int minutes) {
+    return minutes > 0 && hours < 10 && days == 0;
+  }
+
+  private void addSpaceIfNeeded(StringBuilder message) {
+    if (message.length() > 0) {
+      message.append(" ");
+    }
+  }
+
+  private int hoursInDay() {
+    return settings.getInt(CoreProperties.HOURS_IN_DAY);
+  }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/internal/WorkDurationFactory.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/internal/WorkDurationFactory.java
deleted file mode 100644 (file)
index 442f689..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.internal;
-
-import org.sonar.api.BatchComponent;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.ServerComponent;
-import org.sonar.api.config.Settings;
-
-/**
- * @since 4.2
- */
-public final class WorkDurationFactory implements BatchComponent, ServerComponent {
-
-  private final Settings settings;
-
-  public WorkDurationFactory(Settings settings) {
-    this.settings = settings;
-  }
-
-  /**
-   * @deprecated since 4.3
-   */
-  @Deprecated
-  public WorkDuration createFromWorkingValue(int value, WorkDuration.UNIT unit) {
-    return WorkDuration.createFromValueAndUnit(value, unit, hoursInDay());
-  }
-
-  /**
-   * @deprecated since 4.3
-   */
-  @Deprecated
-  public WorkDuration createFromWorkingLong(long duration) {
-    return WorkDuration.createFromLong(duration, hoursInDay());
-  }
-
-  /**
-   * @since 4.3
-   */
-  public WorkDuration createFromMinutes(long duration) {
-    return WorkDuration.createFromMinutes(duration, hoursInDay());
-  }
-
-  private int hoursInDay(){
-    return settings.getInt(CoreProperties.HOURS_IN_DAY);
-  }
-
-}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/DurationsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/DurationsTest.java
new file mode 100644 (file)
index 0000000..5926f49
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.api.i18n.I18n;
+
+import java.util.Locale;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DurationsTest {
+
+  static final int HOURS_IN_DAY = 8;
+
+  static final long ONE_MINUTE = 1L;
+  static final long ONE_HOUR = ONE_MINUTE * 60;
+  static final long ONE_DAY = HOURS_IN_DAY * ONE_HOUR;
+
+  @Mock
+  I18n i18n;
+
+  Locale locale = Locale.ENGLISH;
+
+  Settings settings;
+
+  Durations durations;
+
+  @Before
+  public void setUp() throws Exception {
+    settings = new Settings();
+    settings.setProperty(CoreProperties.HOURS_IN_DAY, HOURS_IN_DAY);
+    durations = new Durations(settings, i18n);
+  }
+
+  @Test
+  public void create_from_minutes() throws Exception {
+    assertThat(durations.create(10L).toMinutes()).isEqualTo(10L);
+  }
+
+  @Test
+  public void decode() throws Exception {
+    // 1 working day -> 8 hours
+    assertThat(durations.decode("1d").toMinutes()).isEqualTo(8L * ONE_HOUR);
+    // 8 hours
+    assertThat(durations.decode("8h").toMinutes()).isEqualTo(8L * ONE_HOUR);
+  }
+
+  @Test
+  public void format() {
+    when(i18n.message(eq(locale), eq("work_duration.x_days"), eq((String) null), eq(5))).thenReturn("5d");
+    when(i18n.message(eq(locale), eq("work_duration.x_hours"), eq((String) null), eq(2))).thenReturn("2h");
+    when(i18n.message(eq(locale), eq("work_duration.x_minutes"), eq((String) null), eq(1))).thenReturn("1min");
+
+    assertThat(durations.format(locale, Duration.create(5 * ONE_DAY), Durations.DurationFormat.SHORT)).isEqualTo("5d");
+    assertThat(durations.format(locale, Duration.create(2 * ONE_HOUR), Durations.DurationFormat.SHORT)).isEqualTo("2h");
+    assertThat(durations.format(locale, Duration.create(ONE_MINUTE), Durations.DurationFormat.SHORT)).isEqualTo("1min");
+
+    assertThat(durations.format(locale, Duration.create(5 * ONE_DAY + 2 * ONE_HOUR), Durations.DurationFormat.SHORT)).isEqualTo("5d 2h");
+    assertThat(durations.format(locale, Duration.create(2 * ONE_HOUR + ONE_MINUTE), Durations.DurationFormat.SHORT)).isEqualTo("2h 1min");
+    assertThat(durations.format(locale, Duration.create(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE), Durations.DurationFormat.SHORT)).isEqualTo("5d 2h");
+  }
+
+  @Test
+  public void not_display_following_element_when_bigger_than_ten() {
+    int hoursInDay = 15;
+    settings.setProperty(CoreProperties.HOURS_IN_DAY, Integer.toString(hoursInDay));
+
+    when(i18n.message(eq(locale), eq("work_duration.x_days"), eq((String) null), eq(15))).thenReturn("15d");
+    when(i18n.message(eq(locale), eq("work_duration.x_hours"), eq((String) null), eq(12))).thenReturn("12h");
+
+    assertThat(durations.format(locale, Duration.create(15 * hoursInDay * ONE_HOUR + 2 * ONE_HOUR + ONE_MINUTE), Durations.DurationFormat.SHORT)).isEqualTo("15d");
+    assertThat(durations.format(locale, Duration.create(12 * ONE_HOUR + ONE_MINUTE), Durations.DurationFormat.SHORT)).isEqualTo("12h");
+  }
+
+  @Test
+  public void display_zero_without_unit() {
+    assertThat(durations.format(locale, Duration.create(0), Durations.DurationFormat.SHORT)).isEqualTo("0");
+  }
+
+  @Test
+  public void display_negative_duration() {
+    when(i18n.message(eq(locale), eq("work_duration.x_days"), eq((String) null), eq(-5))).thenReturn("-5d");
+    when(i18n.message(eq(locale), eq("work_duration.x_hours"), eq((String) null), eq(-2))).thenReturn("-2h");
+    when(i18n.message(eq(locale), eq("work_duration.x_minutes"), eq((String) null), eq(-1))).thenReturn("-1min");
+
+    assertThat(durations.format(locale, Duration.create(-5 * ONE_DAY), Durations.DurationFormat.SHORT)).isEqualTo("-5d");
+    assertThat(durations.format(locale, Duration.create(-2 * ONE_HOUR), Durations.DurationFormat.SHORT)).isEqualTo("-2h");
+    assertThat(durations.format(locale, Duration.create(-1 * ONE_MINUTE), Durations.DurationFormat.SHORT)).isEqualTo("-1min");
+  }
+
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/internal/WorkDurationFactoryTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/internal/WorkDurationFactoryTest.java
deleted file mode 100644 (file)
index 735ab46..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.internal;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Settings;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class WorkDurationFactoryTest {
-
-  WorkDurationFactory factory;
-
-  static final int HOURS_IN_DAY = 8;
-  static final Long ONE_HOUR_IN_MINUTES = 1L * 60;
-
-  @Before
-  public void setUp() throws Exception {
-    Settings settings = new Settings();
-    settings.setProperty(CoreProperties.HOURS_IN_DAY, HOURS_IN_DAY);
-    factory = new WorkDurationFactory(settings);
-  }
-
-  @Test
-  public void create_from_working_value() throws Exception {
-    // 1 working day -> 8 hours
-    assertThat(factory.createFromWorkingValue(1, WorkDuration.UNIT.DAYS).toMinutes()).isEqualTo(8L * ONE_HOUR_IN_MINUTES);
-    // 8 hours
-    assertThat(factory.createFromWorkingValue(8, WorkDuration.UNIT.HOURS).toMinutes()).isEqualTo(8L * ONE_HOUR_IN_MINUTES);
-  }
-
-  @Test
-  public void create_from_working_long() throws Exception {
-    WorkDuration workDuration = factory.createFromWorkingLong(1l);
-    assertThat(workDuration.days()).isEqualTo(0);
-    assertThat(workDuration.hours()).isEqualTo(0);
-    assertThat(workDuration.minutes()).isEqualTo(1);
-  }
-
-  @Test
-  public void create_from_seconds() throws Exception {
-    WorkDuration workDuration = factory.createFromMinutes(8L * ONE_HOUR_IN_MINUTES);
-    assertThat(workDuration.days()).isEqualTo(1);
-    assertThat(workDuration.hours()).isEqualTo(0);
-    assertThat(workDuration.minutes()).isEqualTo(0);
-  }
-}
index d87e499e3e25e4c363c5451f89285bb620ea7b8b..b8b86d3e60bea52c3e50bf81fdb6732df6b0b671 100644 (file)
@@ -23,6 +23,7 @@ import org.sonar.api.ServerComponent;
 import org.sonar.api.i18n.I18n;
 import org.sonar.api.issue.internal.FieldDiffs;
 import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
 import org.sonar.core.issue.IssueUpdater;
 import org.sonar.server.user.UserSession;
 
@@ -38,9 +39,11 @@ public class IssueChangelogFormatter implements ServerComponent {
   private static final String ISSUE_CHANGELOG_FIELD = "issue.changelog.field.";
 
   private final I18n i18n;
+  private final Durations durations;
 
-  public IssueChangelogFormatter(I18n i18n) {
+  public IssueChangelogFormatter(I18n i18n, Durations durations) {
     this.i18n = i18n;
+    this.durations = durations;
   }
 
   public List<String> format(Locale locale, FieldDiffs diffs) {
@@ -72,10 +75,10 @@ public class IssueChangelogFormatter implements ServerComponent {
     String oldValueString = oldValue != null && !"".equals(oldValue) ? oldValue.toString() : null;
     if (IssueUpdater.TECHNICAL_DEBT.equals(key)) {
       if (newValueString != null) {
-        newValueString = i18n.formatWorkDuration(UserSession.get().locale(), Duration.create(Long.parseLong(newValueString)));
+        newValueString = durations.format(UserSession.get().locale(), Duration.create(Long.parseLong(newValueString)), Durations.DurationFormat.SHORT);
       }
       if (oldValueString != null) {
-        oldValueString = i18n.formatWorkDuration(UserSession.get().locale(), Duration.create(Long.parseLong(oldValueString)));
+        oldValueString = durations.format(UserSession.get().locale(), Duration.create(Long.parseLong(oldValueString)), Durations.DurationFormat.SHORT);
       }
     }
     return new IssueChangelogDiffFormat(oldValueString, newValueString);
index 116b32da940cd7475fe7ac11eff02e94a1bec588..3a78d88179f5e97f78a8365a9e20debcac265474 100644 (file)
@@ -34,6 +34,7 @@ import org.sonar.api.technicaldebt.server.Characteristic;
 import org.sonar.api.user.User;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.component.ComponentDto;
@@ -64,15 +65,17 @@ public class IssueShowWsHandler implements RequestHandler {
   private final ActionService actionService;
   private final DefaultTechnicalDebtManager technicalDebtManager;
   private final I18n i18n;
+  private final Durations durations;
 
   public IssueShowWsHandler(IssueFinder issueFinder, IssueService issueService, IssueChangelogService issueChangelogService, ActionService actionService,
-                            DefaultTechnicalDebtManager technicalDebtManager, I18n i18n) {
+                            DefaultTechnicalDebtManager technicalDebtManager, I18n i18n, Durations durations) {
     this.issueFinder = issueFinder;
     this.issueService = issueService;
     this.issueChangelogService = issueChangelogService;
     this.actionService = actionService;
     this.technicalDebtManager = technicalDebtManager;
     this.i18n = i18n;
+    this.durations = durations;
   }
 
   @Override
@@ -115,7 +118,7 @@ public class IssueShowWsHandler implements RequestHandler {
       .prop("author", issue.authorLogin())
       .prop("actionPlan", actionPlanKey)
       .prop("actionPlanName", actionPlan != null ? actionPlan.name() : null)
-      .prop("debt", debt != null ? i18n.formatWorkDuration(UserSession.get().locale(), debt) : null)
+      .prop("debt", debt != null ? durations.format(UserSession.get().locale(), debt, Durations.DurationFormat.SHORT) : null)
       .prop("creationDate", DateUtils.formatDateTime(issue.creationDate()))
       .prop("fCreationDate", formatDate(issue.creationDate()))
       .prop("updateDate", updateDate != null ? DateUtils.formatDateTime(updateDate) : null)
index 619cc7b662511386de76493f62aa115e5eb3f84e..4a4f73cb465147e1e109b82833d244a22a771d39 100644 (file)
@@ -32,18 +32,17 @@ import org.sonar.api.resources.Languages;
 import org.sonar.api.resources.ResourceTypes;
 import org.sonar.api.rules.AnnotationRuleParser;
 import org.sonar.api.rules.XMLRuleParser;
+import org.sonar.api.utils.Durations;
 import org.sonar.api.utils.HttpDownloader;
 import org.sonar.api.utils.TimeProfiler;
 import org.sonar.api.utils.UriReader;
 import org.sonar.api.utils.internal.TempFolderCleaner;
-import org.sonar.api.utils.internal.WorkDurationFactory;
 import org.sonar.core.component.SnapshotPerspectives;
 import org.sonar.core.component.db.ComponentDao;
 import org.sonar.core.config.Logback;
 import org.sonar.core.i18n.DefaultI18n;
 import org.sonar.core.i18n.GwtI18n;
 import org.sonar.core.i18n.RuleI18nManager;
-import org.sonar.core.i18n.WorkDurationFormatter;
 import org.sonar.core.issue.IssueFilterSerializer;
 import org.sonar.core.issue.IssueNotifications;
 import org.sonar.core.issue.IssueUpdater;
@@ -221,8 +220,7 @@ public final class Platform {
     rootContainer.addSingleton(DefaultI18n.class);
     rootContainer.addSingleton(RuleI18nManager.class);
     rootContainer.addSingleton(GwtI18n.class);
-    rootContainer.addSingleton(WorkDurationFormatter.class);
-    rootContainer.addSingleton(WorkDurationFactory.class);
+    rootContainer.addSingleton(Durations.class);
 
     rootContainer.addSingleton(PreviewDatabaseFactory.class);
     rootContainer.addSingleton(SemaphoreUpdater.class);
index 8496e53051c67dc3c69e3d8db298bfefa20edd1a..55b2cb52a4f1971b66782971568abbb770452d44 100644 (file)
@@ -22,8 +22,6 @@ package org.sonar.server.technicaldebt;
 
 import org.sonar.api.ServerComponent;
 import org.sonar.api.technicaldebt.server.Characteristic;
-import org.sonar.api.utils.internal.WorkDuration;
-import org.sonar.api.utils.internal.WorkDurationFactory;
 import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager;
 
 import javax.annotation.CheckForNull;
@@ -31,20 +29,14 @@ import javax.annotation.CheckForNull;
 import java.util.List;
 
 /**
- * Used through ruby code <pre>Internal.technical_debt</pre>
+ * Used through ruby code <pre>Internal.debt</pre>
  */
 public class DebtService implements ServerComponent {
 
   private final DefaultTechnicalDebtManager finder;
-  private final WorkDurationFactory workDurationFactory;
 
-  public DebtService(DefaultTechnicalDebtManager finder, WorkDurationFactory workDurationFactory) {
+  public DebtService(DefaultTechnicalDebtManager finder) {
     this.finder = finder;
-    this.workDurationFactory = workDurationFactory;
-  }
-
-  public WorkDuration toWorkDuration(long debt) {
-    return workDurationFactory.createFromMinutes(debt);
   }
 
   public List<Characteristic> findRootCharacteristics() {
index 80a6ba85320273cd6501c72004a3874c7c445db6..b0a2eb4061e08f1c9e3b18c530f6f8680ee57900 100644 (file)
@@ -24,6 +24,7 @@ import org.apache.commons.lang.StringUtils;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.i18n.I18n;
 import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
 import org.sonar.core.i18n.GwtI18n;
 import org.sonar.server.user.UserSession;
 
@@ -39,11 +40,13 @@ import java.util.Map;
 public class JRubyI18n implements ServerComponent {
 
   private I18n i18n;
+  private Durations durations;
   private Map<String, Locale> localesByRubyKey = Maps.newHashMap();
   private GwtI18n gwtI18n;
 
-  public JRubyI18n(I18n i18n, GwtI18n gwtI18n) {
+  public JRubyI18n(I18n i18n, Durations durations, GwtI18n gwtI18n) {
     this.i18n = i18n;
+    this.durations = durations;
     this.gwtI18n = gwtI18n;
   }
 
@@ -87,12 +90,12 @@ public class JRubyI18n implements ServerComponent {
     return i18n.ageFromNow(UserSession.get().locale(), date);
   }
 
-  public String formatWorkDuration(Duration duration) {
-    return i18n.formatWorkDuration(UserSession.get().locale(), duration);
+  public String formatDuration(Duration duration, String format) {
+    return durations.format(UserSession.get().locale(), duration, Durations.DurationFormat.valueOf(format));
   }
 
-  public String formatLongWorkDuration(long duration) {
-    return formatWorkDuration(Duration.create(duration));
+  public String formatLongDuration(long duration, String format) {
+    return formatDuration(Duration.create(duration), format);
   }
 
 }
index a9b04bcd49b29691b26d1367b65e89b3932c8171..155e122d4e6d9b2100fcf321227bb8a1a05e6d95 100644 (file)
@@ -187,11 +187,11 @@ class IssueController < ApplicationController
     require_parameters :id
     rule_key = params[:id].split(':')
     @rule = Rule.first(:conditions => ['plugin_name=? and plugin_rule_key=?', rule_key[0], rule_key[1]])
-    @requirement = Internal.technical_debt.findRequirementByRuleId(@rule.id)
+    @requirement = Internal.debt.findRequirementByRuleId(@rule.id)
     # Requirement can be null if it's disabled or if there's no requirement on this rule
     if @requirement
-      @characteristic = Internal.technical_debt.findCharacteristic(@requirement.parentId)
-      @root_characteristic = Internal.technical_debt.findCharacteristic(@requirement.rootId)
+      @characteristic = Internal.debt.findCharacteristic(@requirement.parentId)
+      @root_characteristic = Internal.debt.findCharacteristic(@requirement.rootId)
     end
     render :partial => 'issue/rule'
   end
index b169243bb19682c74968dab48dd6865f553c9735..c986b8101f0ef75a16b3361c05ab70ccfc3d607c 100644 (file)
@@ -54,7 +54,7 @@ class Internal
     component(Java::OrgSonarServerPermission::InternalPermissionTemplateService.java_class)
   end
 
-  def self.technical_debt
+  def self.debt
     component(Java::OrgSonarServerTechnicaldebt::DebtService.java_class)
   end
 
@@ -86,6 +86,10 @@ class Internal
     component(Java::OrgSonarServerRule::RuleTags.java_class)
   end
 
+  def self.durations
+    component(Java::OrgSonarApiUtils::Durations.java_class)
+  end
+
   def self.i18n
     component(Java::OrgSonarServerUi::JRubyI18n.java_class)
   end
index 2c99796de442089dcc282e3253d264ae94ae8f86..4d05132ced825c8e4c43d3574ec29548f915fec1 100644 (file)
@@ -34,7 +34,7 @@ class Issue
     hash[:message] = issue.message if issue.message
     hash[:line] = issue.line.to_i if issue.line
     hash[:effortToFix] = issue.effortToFix.to_f if issue.effortToFix
-    hash[:technicalDebt] = debt_to_hash(issue.debt) if issue.debt
+    hash[:debt] = Internal.durations.encode(issue.debt) if issue.debt
     hash[:reporter] = issue.reporter if issue.reporter
     hash[:assignee] = issue.assignee if issue.assignee
     hash[:author] = issue.authorLogin if issue.authorLogin
@@ -75,8 +75,8 @@ class Issue
         hash_diff = {}
         hash_diff[:key] = key
         if key == 'technicalDebt'
-          hash_diff[:newValue] = long_debt_to_hash(diff.newValue()) if diff.newValue.present?
-          hash_diff[:oldValue] = long_debt_to_hash(diff.oldValue()) if diff.oldValue.present?
+          hash_diff[:newValue] = Internal.durations.encode(Internal.durations.create(diff.newValue().to_i)) if diff.newValue.present?
+          hash_diff[:oldValue] = Internal.durations.encode(Internal.durations.create(diff.oldValue().to_i)) if diff.oldValue.present?
         else
           hash_diff[:newValue] = diff.newValue() if diff.newValue.present?
           hash_diff[:oldValue] = diff.oldValue() if diff.oldValue.present?
@@ -89,21 +89,4 @@ class Issue
     hash
   end
 
-
-  private
-
-  def self.debt_to_hash(debt)
-    long_debt_to_hash(debt.toMinutes)
-  end
-
-  def self.long_debt_to_hash(debt)
-    # TODO do not use toWorkDuration() but calculate days, hours and minutes from the duration and from hoursInDay property
-    work_duration = Internal.technical_debt.toWorkDuration(debt.to_i)
-    {
-        :days => work_duration.days(),
-        :hours => work_duration.hours(),
-        :minutes => work_duration.minutes()
-    }
-  end
-
 end
index 0198b35e57aa47fe4d39e3b0143aba7281f748bc..d60697245a13a57e64dae6dec345ca6a744ad68d 100644 (file)
@@ -164,7 +164,7 @@ class ProjectMeasure < ActiveRecord::Base
   end
 
   def work_duration_formatted_value(value)
-    Internal.i18n.formatLongWorkDuration(value.to_i)
+    Internal.i18n.formatLongDuration(value.to_i, 'SHORT')
   end
 
   def color
index 304bf918f5b63043a06241d0f164b8908bbbf7b3..7f15477920daf2c8b9695d0c1d1ec73a89a4ccf4 100644 (file)
@@ -96,7 +96,7 @@
       <% end %>
     <% end %>
     <% if issue.debt %>
-      <li><%= message('issue.debt') -%>&nbsp;<%= Internal.i18n.formatWorkDuration(issue.debt) -%></li>
+      <li><%= message('issue.debt') -%>&nbsp;<%= Internal.i18n.formatDuration(issue.debt, 'SHORT') -%></li>
     <% end %>
     <% if issue.authorLogin %>
       <li><%= message('issue.authorLogin') -%>&nbsp;<%= issue.authorLogin -%></li>
index 98323c582b645266a2818c12d1ca542c7973faa5..a6c35a51f28e6c09ae094be2932583141f4614b4 100644 (file)
@@ -27,6 +27,7 @@ import org.mockito.runners.MockitoJUnitRunner;
 import org.sonar.api.i18n.I18n;
 import org.sonar.api.issue.internal.FieldDiffs;
 import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
 
 import java.util.List;
 import java.util.Locale;
@@ -44,11 +45,15 @@ public class IssueChangelogFormatterTest {
   @Mock
   private I18n i18n;
 
+  @Mock
+  private Durations durations;
+
+
   private IssueChangelogFormatter formatter;
 
   @Before
   public void before() {
-    formatter = new IssueChangelogFormatter(i18n);
+    formatter = new IssueChangelogFormatter(i18n, durations);
   }
 
   @Test
@@ -128,8 +133,8 @@ public class IssueChangelogFormatterTest {
     FieldDiffs diffs = new FieldDiffs();
     diffs.setDiff("technicalDebt", "18000", "28800");
 
-    when(i18n.formatWorkDuration(any(Locale.class), eq(Duration.create(18000L)))).thenReturn("5 hours");
-    when(i18n.formatWorkDuration(any(Locale.class), eq(Duration.create(28800L)))).thenReturn("1 days");
+    when(durations.format(any(Locale.class), eq(Duration.create(18000L)), eq(Durations.DurationFormat.SHORT))).thenReturn("5 hours");
+    when(durations.format(any(Locale.class), eq(Duration.create(28800L)), eq(Durations.DurationFormat.SHORT))).thenReturn("1 days");
 
     when(i18n.message(DEFAULT_LOCALE, "issue.changelog.field.technicalDebt", null)).thenReturn("Technical Debt");
     when(i18n.message(DEFAULT_LOCALE, "issue.changelog.changed_to", null, "Technical Debt", "1 days")).thenReturn("Technical Debt changed to 1 days");
@@ -146,7 +151,7 @@ public class IssueChangelogFormatterTest {
     FieldDiffs diffs = new FieldDiffs();
     diffs.setDiff("technicalDebt", null, "28800");
 
-    when(i18n.formatWorkDuration(any(Locale.class), eq(Duration.create(28800L)))).thenReturn("1 days");
+    when(durations.format(any(Locale.class), eq(Duration.create(28800L)), eq(Durations.DurationFormat.SHORT))).thenReturn("1 days");
 
     when(i18n.message(DEFAULT_LOCALE, "issue.changelog.field.technicalDebt", null)).thenReturn("Technical Debt");
     when(i18n.message(DEFAULT_LOCALE, "issue.changelog.changed_to", null, "Technical Debt", "1 days")).thenReturn("Technical Debt changed to 1 days");
index 2dc944f55344fcb8eb5644fd49d6bb3db0b56ac4..5d9c8edeb542cea44f5ec1214c826ba9838c2cdb 100644 (file)
@@ -43,6 +43,7 @@ import org.sonar.api.technicaldebt.server.internal.DefaultCharacteristic;
 import org.sonar.api.user.User;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.component.ComponentDto;
 import org.sonar.core.issue.DefaultActionPlan;
@@ -89,6 +90,9 @@ public class IssueShowWsHandlerTest {
   @Mock
   I18n i18n;
 
+  @Mock
+  Durations durations;
+
   List<Issue> issues;
   DefaultIssueQueryResult result;
 
@@ -110,7 +114,7 @@ public class IssueShowWsHandlerTest {
 
     when(i18n.message(any(Locale.class), eq("created"), eq((String) null))).thenReturn("Created");
 
-    tester = new WsTester(new IssuesWs(new IssueShowWsHandler(issueFinder, issueService, issueChangelogService, actionService, technicalDebtManager, i18n)));
+    tester = new WsTester(new IssuesWs(new IssueShowWsHandler(issueFinder, issueService, issueChangelogService, actionService, technicalDebtManager, i18n, durations)));
   }
 
   @Test
@@ -307,7 +311,7 @@ public class IssueShowWsHandlerTest {
     Issue issue = createStandardIssue().setDebt(debt);
     issues.add(issue);
 
-    when(i18n.formatWorkDuration(any(Locale.class), eq(debt))).thenReturn("2 hours 1 minutes");
+    when(durations.format(any(Locale.class), eq(debt), eq(Durations.DurationFormat.SHORT))).thenReturn("2 hours 1 minutes");
 
     MockUserSession.set();
     WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
index f60b78dae01b8d262aa35eda471f9b28e927ffe1..b66fc6c26f58329cc31d4c5a4b53632783751e9e 100644 (file)
@@ -21,12 +21,8 @@ package org.sonar.server.technicaldebt;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Settings;
 import org.sonar.api.technicaldebt.server.Characteristic;
 import org.sonar.api.technicaldebt.server.internal.DefaultCharacteristic;
-import org.sonar.api.utils.internal.WorkDuration;
-import org.sonar.api.utils.internal.WorkDurationFactory;
 import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager;
 
 import java.util.List;
@@ -37,21 +33,13 @@ import static org.mockito.Mockito.*;
 
 public class DebtServiceTest {
 
-  private static final int HOURS_IN_DAY = 8;
   DefaultTechnicalDebtManager finder = mock(DefaultTechnicalDebtManager.class);
 
   DebtService service;
 
   @Before
   public void setUp() throws Exception {
-    Settings settings = new Settings();
-    settings.setProperty(CoreProperties.HOURS_IN_DAY, HOURS_IN_DAY);
-    service = new DebtService(finder, new WorkDurationFactory(settings));
-  }
-
-  @Test
-  public void to_work_duration() {
-    assertThat(service.toWorkDuration(60L * HOURS_IN_DAY)).isEqualTo(WorkDuration.createFromValueAndUnit(1, WorkDuration.UNIT.DAYS, HOURS_IN_DAY));
+    service = new DebtService(finder);
   }
 
   @Test
index ef26d5c0209c83e2430fe8788bf62fd371b57279..a66ecac13706a02def48baa51bbc1fe9a7502628 100644 (file)
@@ -26,6 +26,7 @@ import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 import org.sonar.api.i18n.I18n;
 import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
 import org.sonar.core.i18n.GwtI18n;
 
 import java.util.Date;
@@ -45,12 +46,15 @@ public class JRubyI18nTest {
   @Mock
   GwtI18n gwtI18n;
 
+  @Mock
+  Durations durations;
+
   JRubyI18n jRubyI18n;
 
 
   @Before
   public void setUp() throws Exception {
-    jRubyI18n = new JRubyI18n(i18n, gwtI18n);
+    jRubyI18n = new JRubyI18n(i18n, durations, gwtI18n);
   }
 
   @Test
@@ -95,14 +99,14 @@ public class JRubyI18nTest {
 
   @Test
   public void format_work_duration() throws Exception {
-    jRubyI18n.formatWorkDuration(Duration.create(10L));
-    verify(i18n).formatWorkDuration(any(Locale.class), eq(Duration.create(10L)));
+    jRubyI18n.formatDuration(Duration.create(10L), "SHORT");
+    verify(durations).format(any(Locale.class), eq(Duration.create(10L)), eq(Durations.DurationFormat.SHORT));
   }
 
   @Test
   public void format_long_work_duration() throws Exception {
-    jRubyI18n.formatLongWorkDuration(10L);
-    verify(i18n).formatWorkDuration(any(Locale.class), eq(Duration.create(10L)));
+    jRubyI18n.formatLongDuration(10L, "SHORT");
+    verify(durations).format(any(Locale.class), eq(Duration.create(10L)), eq(Durations.DurationFormat.SHORT));
   }
 
 }
index 5f0c825ca503c518a4faea8b324ec82b256e90a0..fe396834f22a887a378517ef2a803d6140349757 100644 (file)
@@ -55,7 +55,7 @@ public interface Issue {
   Double effortToFix();
 
   @CheckForNull
-  WorkDayDuration technicalDebt();
+  String debt();
 
   String status();
 
diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/WorkDayDuration.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/WorkDayDuration.java
deleted file mode 100644 (file)
index 1db322b..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.wsclient.issue;
-
-/**
- * @since 4.0
- */
-public interface WorkDayDuration {
-
-  Integer days();
-
-  Integer minutes();
-
-  Integer hours();
-
-}
index 75c1d77692533239853a61aa1f02a5f9b62afe1f..cbe7005e0741c361ed990638135ec7f3a4001af7 100644 (file)
@@ -21,7 +21,6 @@ package org.sonar.wsclient.issue.internal;
 
 import org.sonar.wsclient.issue.Issue;
 import org.sonar.wsclient.issue.IssueComment;
-import org.sonar.wsclient.issue.WorkDayDuration;
 import org.sonar.wsclient.unmarshallers.JsonUtils;
 
 import javax.annotation.CheckForNull;
@@ -82,12 +81,8 @@ public class DefaultIssue implements Issue {
   }
 
   @CheckForNull
-  public WorkDayDuration technicalDebt() {
-    Map technicalDebt = (Map) json.get("technicalDebt");
-    if (technicalDebt != null) {
-      return new DefaultWorkDayDuration(technicalDebt);
-    }
-    return null;
+  public String debt() {
+    return JsonUtils.getString(json, "debt");
   }
 
   public String status() {
@@ -147,7 +142,7 @@ public class DefaultIssue implements Issue {
   }
 
   public Map<String, String> attributes() {
-    Map<String, String> attr = (Map<String,String>) json.get("attr");
+    Map<String, String> attr = (Map<String, String>) json.get("attr");
     if (attr == null) {
       return Collections.emptyMap();
     }
index 289d3dd62e4751366f3d812687c4464f252f3e74..e357076fa1b69b88be2d56d6ad7b09a7beb05754 100644 (file)
@@ -53,19 +53,7 @@ public class DefaultIssueChangeDiff implements IssueChangeDiff {
   }
 
   private Object parseValue(String attribute) {
-    if ("technicalDebt".equals(key())) {
-      return parseDefaultTechnicalDebt(attribute);
-    } else {
-      return JsonUtils.getString(json, attribute);
-    }
-  }
-
-  private DefaultWorkDayDuration parseDefaultTechnicalDebt(String attribute){
-    Map technicalDebt = (Map) json.get(attribute);
-    if (technicalDebt != null) {
-      return new DefaultWorkDayDuration(technicalDebt);
-    }
-    return null;
+    return JsonUtils.getString(json, attribute);
   }
 
 }
diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultWorkDayDuration.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultWorkDayDuration.java
deleted file mode 100644 (file)
index 7e2ecc7..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.wsclient.issue.internal;
-
-import org.sonar.wsclient.issue.WorkDayDuration;
-import org.sonar.wsclient.unmarshallers.JsonUtils;
-
-import java.util.Map;
-
-/**
- * @since 4.0
- */
-public class DefaultWorkDayDuration implements WorkDayDuration {
-
-  private final Map json;
-
-  DefaultWorkDayDuration(Map json) {
-    this.json = json;
-  }
-
-  public Integer days() {
-    return JsonUtils.getInteger(json, "days");
-  }
-
-  public Integer hours() {
-    return JsonUtils.getInteger(json, "hours");
-  }
-
-  public Integer minutes() {
-    return JsonUtils.getInteger(json, "minutes");
-  }
-
-}
index 5440852389b9c49d9f9148ae998b78a45596ebe7..b0f5de37056f2aa0aa8f9e5b6d2551490943ef2e 100644 (file)
@@ -52,7 +52,7 @@ public class IssueJsonParserTest {
     assertThat(first.assignee()).isEqualTo("karadoc");
     assertThat(first.message()).isEqualTo("the message");
     assertThat(first.effortToFix()).isEqualTo(4.2);
-    assertThat(first.technicalDebt()).isNull();
+    assertThat(first.debt()).isNull();
     assertThat(first.reporter()).isEqualTo("perceval");
     assertThat(first.author()).isEqualTo("pirlouis");
     assertThat(first.actionPlan()).isEqualTo("9450b10c-e725-48b8-bf01-acdec751c491");
@@ -68,7 +68,7 @@ public class IssueJsonParserTest {
     assertThat(second.key()).isEqualTo("FGHIJ");
     assertThat(second.line()).isNull();
     assertThat(second.effortToFix()).isNull();
-    assertThat(second.technicalDebt()).isNull();
+    assertThat(second.debt()).isNull();
     assertThat(second.reporter()).isNull();
     assertThat(second.author()).isNull();
     assertThat(second.attribute("JIRA")).isNull();
@@ -207,9 +207,7 @@ public class IssueJsonParserTest {
     assertThat(issues.size()).isEqualTo(1);
 
     Issue issue = issues.list().get(0);
-    assertThat(issue.technicalDebt().days()).isEqualTo(3);
-    assertThat(issue.technicalDebt().hours()).isEqualTo(0);
-    assertThat(issue.technicalDebt().minutes()).isEqualTo(10);
+    assertThat(issue.debt()).isEqualTo("3d10min");
   }
 
 
@@ -255,16 +253,8 @@ public class IssueJsonParserTest {
     assertThat(change.diffs()).hasSize(1);
     IssueChangeDiff changeDiff = change.diffs().get(0);
     assertThat(changeDiff.key()).isEqualTo("technicalDebt");
-
-    WorkDayDuration newTechnicalDebt = (WorkDayDuration) changeDiff.newValue();
-    assertThat(newTechnicalDebt.days()).isEqualTo(2);
-    assertThat(newTechnicalDebt.hours()).isEqualTo(1);
-    assertThat(newTechnicalDebt.minutes()).isEqualTo(0);
-
-    WorkDayDuration oldTechnicalDebt = (WorkDayDuration) changeDiff.oldValue();
-    assertThat(oldTechnicalDebt.days()).isEqualTo(3);
-    assertThat(oldTechnicalDebt.hours()).isEqualTo(0);
-    assertThat(oldTechnicalDebt.minutes()).isEqualTo(10);
+    assertThat(changeDiff.newValue()).isEqualTo("2d1h");
+    assertThat(changeDiff.oldValue()).isEqualTo("3d10min");
   }
 
   @Test
@@ -280,14 +270,8 @@ public class IssueJsonParserTest {
     assertThat(change.diffs()).hasSize(1);
     IssueChangeDiff changeDiff = change.diffs().get(0);
     assertThat(changeDiff.key()).isEqualTo("technicalDebt");
-
-    WorkDayDuration newTechnicalDebt = (WorkDayDuration) changeDiff.newValue();
-    assertThat(newTechnicalDebt.days()).isEqualTo(2);
-    assertThat(newTechnicalDebt.hours()).isEqualTo(1);
-    assertThat(newTechnicalDebt.minutes()).isEqualTo(0);
-
-    WorkDayDuration oldTechnicalDebt = (WorkDayDuration) changeDiff.oldValue();
-    assertThat(oldTechnicalDebt).isNull();
+    assertThat(changeDiff.newValue()).isEqualTo("2d1h");
+    assertThat(changeDiff.oldValue()).isNull();
   }
 
   @Test
index 8600644db316af7761524b0013bc8827bbce4d8a..645c9eb2dacfc38505b9bc1d67ac39ffd0853c25 100644 (file)
@@ -6,11 +6,7 @@
       "diffs": [
         {
           "key": "technicalDebt",
-          "newValue": {
-            "days": 2,
-            "hours": 1,
-            "minutes": 0
-          }
+          "newValue": "2d1h"
         }
       ]
     }
index 980a523216c09ad0b2485c3b47cb4b7eae713a58..3989c989ad65a8b8ee13c1427ac8119d9beb2d3a 100644 (file)
@@ -6,16 +6,8 @@
       "diffs": [
         {
           "key": "technicalDebt",
-          "oldValue": {
-            "days": 3,
-            "hours": 0,
-            "minutes": 10
-          },
-          "newValue": {
-            "days": 2,
-            "hours": 1,
-            "minutes": 0
-          }
+          "oldValue": "3d10min",
+          "newValue": "2d1h"
         }
       ]
     }
index cda81fc22625f6aaed5b343a5cc4fced72a25020..1da214fac674d7586a6f330c0a0657b97a1d2048 100644 (file)
@@ -7,11 +7,7 @@
             "rule": "squid:CycleBetweenPackages",
             "severity": "CRITICAL",
             "status": "OPEN",
-            "technicalDebt": {
-                    "days": 3,
-                    "hours": 0,
-                    "minutes": 10
-             }
+            "debt": "3d10min"
         }
     ],
     "rules": [