]> source.dussan.org Git - sonarqube.git/commitdiff
Merge WorkDayDuration with WorkUnit
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 10 Feb 2014 17:08:00 +0000 (18:08 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 10 Feb 2014 17:08:20 +0000 (18:08 +0100)
43 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecorator.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecoratorTest.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/technicaldebt/TechnicalDebtCalculator.java
sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
sonar-batch/src/test/java/org/sonar/batch/technicaldebt/TechnicalDebtCalculatorTest.java
sonar-batch/src/test/java/org/sonar/batch/technicaldebt/TechnicalDebtModelLoaderTest.java
sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java
sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java
sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtConverter.java [deleted file]
sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java
sonar-core/src/test/java/org/sonar/core/issue/db/IssueDtoTest.java
sonar-core/src/test/java/org/sonar/core/issue/db/IssueStorageTest.java
sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtManagerTest.java
sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModelTest.java
sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtConverterTest.java [deleted file]
sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtModelSynchronizerTest.java
sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtXMLImporterTest.java
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java
sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/WorkDayDuration.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/internal/DefaultRequirement.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/WorkUnit.java
sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java
sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/WorkDayDurationTest.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/technicaldebt/batch/internal/DefaultRequirementTest.java
sonar-plugin-api/src/test/java/org/sonar/api/technicaldebt/server/internal/DefaultCharacteristicTest.java
sonar-plugin-api/src/test/java/org/sonar/api/utils/WorkUnitTest.java
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/DebtFormatter.java
sonar-server/src/main/java/org/sonar/server/technicaldebt/DebtService.java
sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java
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/DebtFormatterTest.java
sonar-server/src/test/java/org/sonar/server/technicaldebt/DebtServiceTest.java

index 7bc015f104d1226bb33d047e4f33f0cd98442ba5..a4539278b059e73118e8aca8e68c25e29562e5a5 100644 (file)
@@ -25,7 +25,6 @@ import org.sonar.api.checks.NoSonarFilter;
 import org.sonar.api.config.PropertyDefinition;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.batch.components.PastSnapshotFinder;
-import org.sonar.core.technicaldebt.TechnicalDebtConverter;
 import org.sonar.core.timemachine.Periods;
 import org.sonar.plugins.core.batch.IndexProjectPostJob;
 import org.sonar.plugins.core.charts.DistributionAreaChart;
@@ -290,7 +289,6 @@ public final class CorePlugin extends SonarPlugin {
       NewFalsePositiveNotificationDispatcher.newMetadata(),
 
       // technical debt
-      TechnicalDebtConverter.class,
       TechnicalDebtDecorator.class,
       NewTechnicalDebtDecorator.class,
 
index 90603e96ce7ba1c1ebe3684a8e322a7b14338d11..3c568ae78f91dfd0e04078da2d1604a85347bcff 100644 (file)
@@ -31,7 +31,6 @@ import org.sonar.api.issue.Issuable;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.issue.internal.IssueChangeContext;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.profiles.RulesProfile;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
@@ -40,6 +39,7 @@ import org.sonar.api.rules.ActiveRule;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.rules.RuleFinder;
 import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.batch.issue.IssueCache;
 import org.sonar.batch.scan.LastSnapshots;
 import org.sonar.core.issue.IssueUpdater;
@@ -178,7 +178,7 @@ public class IssueTrackingDecorator implements Decorator {
       updater.setPastMessage(issue, ref.getMessage(), changeContext);
       updater.setPastEffortToFix(issue, ref.getEffortToFix(), changeContext);
       Long technicalDebt = ref.getTechnicalDebt();
-      WorkDayDuration previousTechnicalDebt = technicalDebt != null ? WorkDayDuration.fromLong(technicalDebt) : null;
+      WorkUnit previousTechnicalDebt = technicalDebt != null ? WorkUnit.fromLong(technicalDebt) : null;
       updater.setPastTechnicalDebt(issue, previousTechnicalDebt, changeContext);
     }
   }
index 641d659c25e2d0dd1cfc304c18a37e4403055867..cd540b555257c43e2f4c40595fb9886b22a8c1ba 100644 (file)
@@ -24,23 +24,24 @@ import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Ordering;
 import org.apache.commons.lang.time.DateUtils;
+import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.*;
 import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.config.Settings;
 import org.sonar.api.issue.Issuable;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Measure;
 import org.sonar.api.measures.MeasureUtils;
 import org.sonar.api.measures.Metric;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.batch.components.Period;
 import org.sonar.batch.components.TimeMachineConfiguration;
 import org.sonar.core.issue.IssueUpdater;
-import org.sonar.core.technicaldebt.TechnicalDebtConverter;
 
 import javax.annotation.Nullable;
 
@@ -56,12 +57,13 @@ public final class NewTechnicalDebtDecorator implements Decorator {
 
   private final ResourcePerspectives perspectives;
   private final TimeMachineConfiguration timeMachineConfiguration;
-  private final TechnicalDebtConverter technicalDebtConverter;
 
-  public NewTechnicalDebtDecorator(ResourcePerspectives perspectives, TimeMachineConfiguration timeMachineConfiguration, TechnicalDebtConverter technicalDebtConverter) {
+  private final int hoursInDay;
+
+  public NewTechnicalDebtDecorator(ResourcePerspectives perspectives, TimeMachineConfiguration timeMachineConfiguration, Settings settings) {
     this.perspectives = perspectives;
     this.timeMachineConfiguration = timeMachineConfiguration;
-    this.technicalDebtConverter = technicalDebtConverter;
+    this.hoursInDay = settings.getInt(CoreProperties.HOURS_IN_DAY);
   }
 
   public boolean shouldExecuteOnProject(Project project) {
@@ -98,32 +100,34 @@ public final class NewTechnicalDebtDecorator implements Decorator {
   private Double calculateNewTechnicalDebtValue(Collection<Issue> issues, @Nullable Date periodDate) {
     double value = 0;
     for (Issue issue : issues) {
-      WorkDayDuration currentTechnicalDebt = ((DefaultIssue) issue).technicalDebt();
+      double currentTechnicalDebtValue = 0d;
+      WorkUnit currentTechnicalDebt = ((DefaultIssue) issue).technicalDebt();
+      if (currentTechnicalDebt != null) {
+        currentTechnicalDebtValue = currentTechnicalDebt.toDays(hoursInDay);
+      }
 
       Date periodDatePlusOneSecond = periodDate != null ? DateUtils.addSeconds(periodDate, 1) : null;
       if (isAfter(issue.creationDate(), periodDatePlusOneSecond)) {
-        value += technicalDebtConverter.toDays(currentTechnicalDebt);
+        value += currentTechnicalDebtValue;
       } else {
-        value += calculateNewTechnicalDebtValueFromChangelog(currentTechnicalDebt, issue, periodDate);
+        value += calculateNewTechnicalDebtValueFromChangelog(currentTechnicalDebtValue, issue, periodDate);
       }
     }
     return value;
   }
 
-  private double calculateNewTechnicalDebtValueFromChangelog(WorkDayDuration currentTechnicalDebt, Issue issue, Date periodDate) {
-    double currentTechnicalDebtValue = technicalDebtConverter.toDays(currentTechnicalDebt);
-
+  private double calculateNewTechnicalDebtValueFromChangelog(double currentTechnicalDebtValue, Issue issue, Date periodDate) {
     List<FieldDiffs> changelog = technicalDebtHistory(issue);
     for (Iterator<FieldDiffs> iterator = changelog.iterator(); iterator.hasNext(); ) {
       FieldDiffs diff = iterator.next();
       Date date = diff.creationDate();
       if (isLesserOrEqual(date, periodDate)) {
         // return new value from the change that is just before the period date
-        return currentTechnicalDebtValue - technicalDebtConverter.toDays(newValue(diff));
+        return currentTechnicalDebtValue - newValue(diff).toDays(hoursInDay);
       }
       if (!iterator.hasNext()) {
         // return old value from the change that is just after the period date when there's no more element in changelog
-        return currentTechnicalDebtValue - technicalDebtConverter.toDays(oldValue(diff));
+        return currentTechnicalDebtValue - oldValue(diff).toDays(hoursInDay);
       }
     }
     // Return 0 when no changelog
@@ -155,24 +159,24 @@ public final class NewTechnicalDebtDecorator implements Decorator {
     return diffs;
   }
 
-  private WorkDayDuration newValue(FieldDiffs fieldDiffs) {
+  private WorkUnit newValue(FieldDiffs fieldDiffs) {
     for (Map.Entry<String, FieldDiffs.Diff> entry : fieldDiffs.diffs().entrySet()) {
       if (entry.getKey().equals(IssueUpdater.TECHNICAL_DEBT)) {
         Long newValue = entry.getValue().newValueLong();
-        return newValue != null ? WorkDayDuration.fromLong(newValue) : null;
+        return newValue != null ? WorkUnit.fromLong(newValue) : WorkUnit.fromLong(0);
       }
     }
-    return null;
+    return WorkUnit.fromLong(0);
   }
 
-  private WorkDayDuration oldValue(FieldDiffs fieldDiffs) {
+  private WorkUnit oldValue(FieldDiffs fieldDiffs) {
     for (Map.Entry<String, FieldDiffs.Diff> entry : fieldDiffs.diffs().entrySet()) {
       if (entry.getKey().equals(IssueUpdater.TECHNICAL_DEBT)) {
         Long value = entry.getValue().oldValueLong();
-        return value != null ? WorkDayDuration.fromLong(value) : null;
+        return value != null ? WorkUnit.fromLong(value) : WorkUnit.fromLong(0);
       }
     }
-    return null;
+    return WorkUnit.fromLong(0);
   }
 
   private boolean isAfter(@Nullable Date currentDate, @Nullable Date pastDate) {
index 2cbc2ce82858fd94965db8a3e419474f1cc71b32..44eb76e6ba105e6d55666e7cacd09125f009ecbd 100644 (file)
@@ -30,6 +30,7 @@ import org.sonar.api.PropertyType;
 import org.sonar.api.batch.*;
 import org.sonar.api.component.ResourcePerspectives;
 import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.config.Settings;
 import org.sonar.api.issue.Issuable;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.internal.DefaultIssue;
@@ -40,7 +41,7 @@ import org.sonar.api.resources.ResourceUtils;
 import org.sonar.api.technicaldebt.batch.Characteristic;
 import org.sonar.api.technicaldebt.batch.Requirement;
 import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
-import org.sonar.core.technicaldebt.TechnicalDebtConverter;
+import org.sonar.api.utils.WorkUnit;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -60,12 +61,13 @@ public final class TechnicalDebtDecorator implements Decorator {
 
   private final ResourcePerspectives perspectives;
   private final TechnicalDebtModel model;
-  private final TechnicalDebtConverter converter;
 
-  public TechnicalDebtDecorator(ResourcePerspectives perspectives, TechnicalDebtModel model, TechnicalDebtConverter converter) {
+  private final int hoursInDay;
+
+  public TechnicalDebtDecorator(ResourcePerspectives perspectives, TechnicalDebtModel model, Settings settings) {
     this.perspectives = perspectives;
     this.model = model;
-    this.converter = converter;
+    this.hoursInDay = settings.getInt(CoreProperties.HOURS_IN_DAY);
   }
 
   public boolean shouldExecuteOnProject(Project project) {
@@ -168,8 +170,13 @@ public final class TechnicalDebtDecorator implements Decorator {
   private double computeTechnicalDebt(Metric metric, DecoratorContext context, Requirement requirement, Collection<Issue> issues) {
     double value = 0.0;
     if (issues != null) {
-      for (Issue issue : issues){
-        value += converter.toDays(((DefaultIssue) issue).technicalDebt());
+      for (Issue issue : issues) {
+        WorkUnit debt = ((DefaultIssue) issue).technicalDebt();
+        if (debt != null) {
+          value += debt.toDays(hoursInDay);
+        } else {
+          value += 0d;
+        }
       }
     }
 
@@ -200,7 +207,7 @@ public final class TechnicalDebtDecorator implements Decorator {
 
   public static List<PropertyDefinition> definitions() {
     return ImmutableList.of(
-      PropertyDefinition.builder(TechnicalDebtConverter.PROPERTY_HOURS_IN_DAY)
+      PropertyDefinition.builder(CoreProperties.HOURS_IN_DAY)
         .name("Number of working hours in a day")
         .type(PropertyType.INTEGER)
         .defaultValue("8")
index 9a573b3d62aae5993373f857299ef2409afd69d3..0cb689bb9d8000dc499d3b4b29b3c6a4e3054e1f 100644 (file)
@@ -29,7 +29,6 @@ import org.sonar.api.component.ResourcePerspectives;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.issue.internal.IssueChangeContext;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.profiles.RulesProfile;
 import org.sonar.api.resources.File;
 import org.sonar.api.resources.Project;
@@ -37,6 +36,7 @@ import org.sonar.api.resources.Resource;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.batch.issue.IssueCache;
 import org.sonar.batch.scan.LastSnapshots;
 import org.sonar.core.issue.IssueUpdater;
@@ -520,7 +520,7 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
     verify(updater).setPastLine(eq(issue), eq(10));
     verify(updater).setPastMessage(eq(issue), eq("Message"), any(IssueChangeContext.class));
     verify(updater).setPastEffortToFix(eq(issue), eq(1.5), any(IssueChangeContext.class));
-    verify(updater).setPastTechnicalDebt(eq(issue), eq(WorkDayDuration.of(1, 0, 0)), any(IssueChangeContext.class));
+    verify(updater).setPastTechnicalDebt(eq(issue), eq(new WorkUnit.Builder().setMinutes(1).build()), any(IssueChangeContext.class));
   }
 
   @Test
index 2dfc4b3df366ea257a7c7c5906085976421f3e3e..4b9689381360a3aadbf8d371dd2c448d335c29ee 100644 (file)
@@ -27,21 +27,22 @@ import org.junit.runner.RunWith;
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.DecoratorContext;
 import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.config.Settings;
 import org.sonar.api.issue.Issuable;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Measure;
 import org.sonar.api.measures.Metric;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.test.IsMeasure;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.batch.components.Period;
 import org.sonar.batch.components.TimeMachineConfiguration;
-import org.sonar.core.technicaldebt.TechnicalDebtConverter;
 
 import java.util.Date;
 
@@ -67,9 +68,6 @@ public class NewTechnicalDebtDecoratorTest {
   @Mock
   DecoratorContext context;
 
-  @Mock
-  TechnicalDebtConverter technicalDebtConverter;
-
   Date rightNow;
   Date elevenDaysAgo;
   Date tenDaysAgo;
@@ -77,15 +75,14 @@ public class NewTechnicalDebtDecoratorTest {
   Date fiveDaysAgo;
   Date fourDaysAgo;
 
-  WorkDayDuration oneDaysDebt = WorkDayDuration.of(0, 0, 1);
-  WorkDayDuration twoDaysDebt = WorkDayDuration.of(0, 0, 2);
-  WorkDayDuration fiveDaysDebt = WorkDayDuration.of(0, 0, 5);
+  WorkUnit oneDaysDebt = new WorkUnit.Builder().setDays(1).build();
+  WorkUnit twoDaysDebt = new WorkUnit.Builder().setDays(2).build();
+  WorkUnit fiveDaysDebt = new WorkUnit.Builder().setDays(5).build();
 
   @Before
   public void setup() {
-    when(technicalDebtConverter.toDays(oneDaysDebt)).thenReturn(1d);
-    when(technicalDebtConverter.toDays(twoDaysDebt)).thenReturn(2d);
-    when(technicalDebtConverter.toDays(fiveDaysDebt)).thenReturn(5d);
+    Settings settings = new Settings();
+    settings.setProperty(CoreProperties.HOURS_IN_DAY, "8");
 
     ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
     when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
@@ -99,7 +96,7 @@ public class NewTechnicalDebtDecoratorTest {
 
     when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, fiveDaysAgo), new Period(2, tenDaysAgo)));
 
-    decorator = new NewTechnicalDebtDecorator(perspectives, timeMachineConfiguration, technicalDebtConverter);
+    decorator = new NewTechnicalDebtDecorator(perspectives, timeMachineConfiguration, settings);
   }
 
   @Test
@@ -328,7 +325,7 @@ public class NewTechnicalDebtDecoratorTest {
     verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)));
   }
 
-  private Long fromWorkDayDuration(WorkDayDuration workDayDuration){
+  private Long fromWorkDayDuration(WorkUnit workDayDuration) {
     return workDayDuration.toLong();
   }
 
index eb5376d47f0f314d0950148fcd7433a65be85e31..3c13be9a8a30bd0251935e0d662de3d5966f2703 100644 (file)
@@ -28,12 +28,13 @@ import org.junit.runner.RunWith;
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.DecoratorContext;
 import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.config.Settings;
 import org.sonar.api.issue.Issuable;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Measure;
 import org.sonar.api.measures.MeasuresFilter;
@@ -48,7 +49,7 @@ import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
 import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
 import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement;
 import org.sonar.api.test.IsMeasure;
-import org.sonar.core.technicaldebt.TechnicalDebtConverter;
+import org.sonar.api.utils.WorkUnit;
 
 import java.util.List;
 
@@ -66,9 +67,6 @@ public class TechnicalDebtDecoratorTest {
   @Mock
   Resource resource;
 
-  @Mock
-  TechnicalDebtConverter converter;
-
   @Mock
   TechnicalDebtModel defaultTechnicalDebtModel;
 
@@ -82,7 +80,10 @@ public class TechnicalDebtDecoratorTest {
     ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
     when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
 
-    decorator = new TechnicalDebtDecorator(perspectives, defaultTechnicalDebtModel, converter);
+    Settings settings = new Settings();
+    settings.setProperty(CoreProperties.HOURS_IN_DAY, "8");
+
+    decorator = new TechnicalDebtDecorator(perspectives, defaultTechnicalDebtModel, settings);
   }
 
   @Test
@@ -128,8 +129,7 @@ public class TechnicalDebtDecoratorTest {
 
   @Test
   public void add_technical_debt_from_one_issue_and_no_parent() throws Exception {
-    WorkDayDuration technicalDebt = mock(WorkDayDuration.class);
-    when(converter.toDays(technicalDebt)).thenReturn(1.0);
+    WorkUnit technicalDebt = new WorkUnit.Builder().setDays(1).build();
 
     Issue issue = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt);
     when(issuable.issues()).thenReturn(newArrayList(issue));
@@ -144,10 +144,23 @@ public class TechnicalDebtDecoratorTest {
     verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, null, requirement, 1.0)));
   }
 
+  @Test
+  public void add_technical_debt_from_one_issue_without_debt() throws Exception {
+    Issue issue = createIssue("rule1", "repo1").setTechnicalDebt(null);
+    when(issuable.issues()).thenReturn(newArrayList(issue));
+
+    Requirement requirement = mock(Requirement.class);
+    when(defaultTechnicalDebtModel.requirementsByRule(RuleKey.of("repo1", "rule1"))).thenReturn(requirement);
+    doReturn(newArrayList(requirement)).when(defaultTechnicalDebtModel).requirements();
+
+    decorator.decorate(resource, context);
+
+    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, 0.0)));
+  }
+
   @Test
   public void add_technical_debt_from_one_issue_and_propagate_to_parents() throws Exception {
-    WorkDayDuration technicalDebt = mock(WorkDayDuration.class);
-    when(converter.toDays(technicalDebt)).thenReturn(1.0);
+    WorkUnit technicalDebt = new WorkUnit.Builder().setDays(1).build();
 
     Issue issue = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt);
     when(issuable.issues()).thenReturn(newArrayList(issue));
@@ -170,11 +183,8 @@ public class TechnicalDebtDecoratorTest {
 
   @Test
   public void add_technical_debt_from_issues() throws Exception {
-    WorkDayDuration technicalDebt1 = mock(WorkDayDuration.class);
-    when(converter.toDays(technicalDebt1)).thenReturn(1.0);
-
-    WorkDayDuration technicalDebt2 = mock(WorkDayDuration.class);
-    when(converter.toDays(technicalDebt2)).thenReturn(2.0);
+    WorkUnit technicalDebt1 = new WorkUnit.Builder().setDays(1).build();
+    WorkUnit technicalDebt2 = new WorkUnit.Builder().setDays(2).build();
 
     Issue issue1 = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt1);
     Issue issue2 = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt1);
@@ -202,11 +212,10 @@ public class TechnicalDebtDecoratorTest {
 
   @Test
   public void add_technical_debt_from_children_measures() throws Exception {
-    WorkDayDuration technicalDebt1 = mock(WorkDayDuration.class);
-    when(converter.toDays(technicalDebt1)).thenReturn(1.0);
+    WorkUnit technicalDebt = new WorkUnit.Builder().setDays(1).build();
 
-    Issue issue1 = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt1);
-    Issue issue2 = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt1);
+    Issue issue1 = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt);
+    Issue issue2 = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt);
     when(issuable.issues()).thenReturn(newArrayList(issue1, issue2));
 
     DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("rootCharacteristic");
index 285e29b56529e20dfc10632c06f5693b83dbd270..821de088059037898a90f3f7d9064c497c3c4017 100644 (file)
@@ -35,32 +35,10 @@ import org.sonar.batch.DefaultFileLinesContextFactory;
 import org.sonar.batch.DefaultResourceCreationLock;
 import org.sonar.batch.ProjectConfigurator;
 import org.sonar.batch.ProjectTree;
-import org.sonar.batch.bootstrap.BootstrapSettings;
-import org.sonar.batch.bootstrap.ExtensionInstaller;
-import org.sonar.batch.bootstrap.ExtensionMatcher;
-import org.sonar.batch.bootstrap.ExtensionUtils;
-import org.sonar.batch.bootstrap.MetricProvider;
+import org.sonar.batch.bootstrap.*;
 import org.sonar.batch.components.PeriodsDefinition;
-import org.sonar.batch.index.Caches;
-import org.sonar.batch.index.ComponentDataCache;
-import org.sonar.batch.index.ComponentDataPersister;
-import org.sonar.batch.index.DefaultIndex;
-import org.sonar.batch.index.DefaultPersistenceManager;
-import org.sonar.batch.index.DefaultResourcePersister;
-import org.sonar.batch.index.DependencyPersister;
-import org.sonar.batch.index.EventPersister;
-import org.sonar.batch.index.LinkPersister;
-import org.sonar.batch.index.MeasurePersister;
-import org.sonar.batch.index.MemoryOptimizer;
-import org.sonar.batch.index.ResourceCache;
-import org.sonar.batch.index.ResourceKeyMigration;
-import org.sonar.batch.index.SnapshotCache;
-import org.sonar.batch.index.SourcePersister;
-import org.sonar.batch.issue.DefaultProjectIssues;
-import org.sonar.batch.issue.DeprecatedViolations;
-import org.sonar.batch.issue.IssueCache;
-import org.sonar.batch.issue.IssuePersister;
-import org.sonar.batch.issue.ScanIssueStorage;
+import org.sonar.batch.index.*;
+import org.sonar.batch.issue.*;
 import org.sonar.batch.phases.GraphPersister;
 import org.sonar.batch.profiling.PhasesSumUpTimeProfiler;
 import org.sonar.batch.scan.filesystem.InputFileCache;
@@ -77,7 +55,6 @@ import org.sonar.core.issue.IssueUpdater;
 import org.sonar.core.issue.workflow.FunctionExecutor;
 import org.sonar.core.issue.workflow.IssueWorkflow;
 import org.sonar.core.notification.DefaultNotificationManager;
-import org.sonar.core.technicaldebt.TechnicalDebtConverter;
 import org.sonar.core.test.TestPlanBuilder;
 import org.sonar.core.test.TestPlanPerspectiveLoader;
 import org.sonar.core.test.TestableBuilder;
@@ -177,7 +154,6 @@ public class ProjectScanContainer extends ComponentContainer {
 
       // technical debt
       TechnicalDebtModelLoader.class,
-      TechnicalDebtConverter.class,
       TechnicalDebtCalculator.class,
       new TechnicalDebtModelProvider(),
 
index 996cd321c9953ea04d38c8d31a4cbff6e06dd7ac..3cfefd8af44e9caee1da6b3d1831df87d6f1f599 100644 (file)
@@ -21,35 +21,39 @@ package org.sonar.batch.technicaldebt;
 
 import com.google.common.base.Objects;
 import org.sonar.api.BatchExtension;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
 import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.technicaldebt.batch.Requirement;
 import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
 import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement;
 import org.sonar.api.utils.WorkUnit;
-import org.sonar.core.technicaldebt.TechnicalDebtConverter;
 
 /**
  * Computes the remediation cost based on the quality and analysis models.
  */
 public class TechnicalDebtCalculator implements BatchExtension {
 
-  private final TechnicalDebtConverter converter;
+  private int hoursInDay;
+
   private TechnicalDebtModel model;
 
-  public TechnicalDebtCalculator(TechnicalDebtModel model, TechnicalDebtConverter converter) {
+  public TechnicalDebtCalculator(TechnicalDebtModel model, Settings settings) {
     this.model = model;
-    this.converter = converter;
+    this.hoursInDay = settings.getInt(CoreProperties.HOURS_IN_DAY);
   }
 
-  public WorkDayDuration calculTechnicalDebt(Issue issue) {
+  /**
+   * Get the technical debt from the requirement
+   */
+  public WorkUnit calculTechnicalDebt(Issue issue) {
     Requirement requirement = model.requirementsByRule(issue.ruleKey());
     if (requirement != null) {
       if (requirement.function().equals(DefaultRequirement.CONSTANT_ISSUE) && issue.effortToFix() != null) {
-        throw new IllegalArgumentException("Requirement for '"+ issue.ruleKey() +"' can not use 'Constant/issue' remediation function " +
+        throw new IllegalArgumentException("Requirement for '" + issue.ruleKey() + "' can not use 'Constant/issue' remediation function " +
           "because this rule does not have a fixed remediation cost.");
       }
-      return converter.fromMinutes(calculTechnicalDebt(requirement, issue));
+      return fromMinutes(calculTechnicalDebt(requirement, issue));
     }
     return null;
   }
@@ -58,11 +62,45 @@ public class TechnicalDebtCalculator implements BatchExtension {
     long effortToFix = Objects.firstNonNull(issue.effortToFix(), 1L).longValue();
 
     WorkUnit factorUnit = requirement.factor();
-    long factor = factorUnit != null ? converter.toMinutes(factorUnit) : 0L;
+    long factor = factorUnit != null ? toMinutes(factorUnit) : 0L;
 
     WorkUnit offsetUnit = requirement.offset();
-    long offset = offsetUnit != null ? converter.toMinutes(offsetUnit) : 0L;
+    long offset = offsetUnit != null ? toMinutes(offsetUnit) : 0L;
 
     return effortToFix * factor + offset;
   }
+
+  private long toMinutes(WorkUnit factor) {
+    if (factor.days() > 0) {
+      return Double.valueOf(factor.days() * hoursInDay * 60d).longValue();
+    } else if (factor.hours() > 0) {
+      return Double.valueOf(factor.hours() * 60d).longValue();
+    } else {
+      return Double.valueOf(factor.minutes()).longValue();
+    }
+  }
+
+  private WorkUnit fromMinutes(Long inMinutes) {
+    int oneHourInMinute = 60;
+    int days = 0;
+    int hours = 0;
+    int minutes = 0;
+
+    int oneWorkingDay = hoursInDay * oneHourInMinute;
+    if (inMinutes >= oneWorkingDay) {
+      Long nbDays = inMinutes / oneWorkingDay;
+      days = nbDays.shortValue();
+      inMinutes = inMinutes - (nbDays * oneWorkingDay);
+    }
+
+    if (inMinutes >= oneHourInMinute) {
+      Long nbHours = inMinutes / oneHourInMinute;
+      hours = nbHours.shortValue();
+      inMinutes = inMinutes - (nbHours * oneHourInMinute);
+    }
+
+    minutes = inMinutes.shortValue();
+
+    return new WorkUnit.Builder().setDays(days).setHours(hours).setMinutes(minutes).build();
+  }
 }
index 667de9f373a60dd46fa01acdaec09d359d2435f8..532d056a306bebd1477371beeb6242f3a8458274 100644 (file)
@@ -27,7 +27,6 @@ import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.profiles.RulesProfile;
 import org.sonar.api.resources.JavaFile;
 import org.sonar.api.resources.Project;
@@ -36,6 +35,7 @@ import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.*;
 import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.batch.technicaldebt.TechnicalDebtCalculator;
 
 import java.util.Calendar;
@@ -284,14 +284,14 @@ public class ModuleIssuesTest {
       .setRuleKey(SQUID_RULE_KEY)
       .setSeverity(Severity.CRITICAL);
 
-    when(technicalDebtCalculator.calculTechnicalDebt(issue)).thenReturn(WorkDayDuration.of(10, 0, 0));
+    when(technicalDebtCalculator.calculTechnicalDebt(issue)).thenReturn(new WorkUnit.Builder().setDays(10).build());
     when(filters.accept(issue, null)).thenReturn(true);
 
     moduleIssues.initAndAddIssue(issue);
 
     ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
     verify(cache).put(argument.capture());
-    assertThat(argument.getValue().technicalDebt()).isEqualTo(WorkDayDuration.of(10, 0, 0));
+    assertThat(argument.getValue().technicalDebt()).isEqualTo(new WorkUnit.Builder().setDays(10).build());
   }
 
 }
index ccb9554d22516f17fe37e5856703327cc56f62d3..684f44263530032cfb80f2724afbc4c66ca67168 100644 (file)
@@ -25,16 +25,18 @@ import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
 import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
 import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement;
 import org.sonar.api.utils.WorkUnit;
-import org.sonar.core.technicaldebt.TechnicalDebtConverter;
 
 import static org.fest.assertions.Assertions.assertThat;
 import static org.fest.assertions.Fail.fail;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 @RunWith(MockitoJUnitRunner.class)
 public class TechnicalDebtCalculatorTest {
@@ -42,20 +44,17 @@ public class TechnicalDebtCalculatorTest {
   @Mock
   TechnicalDebtModel model;
 
-  @Mock
-  TechnicalDebtConverter converter;
-
-  WorkUnit tenMinutes = WorkUnit.create(10d, WorkUnit.MINUTES);
-  WorkUnit fiveMinutes = WorkUnit.create(5d, WorkUnit.MINUTES);
+  WorkUnit tenMinutes = new WorkUnit.Builder().setMinutes(10).build();
+  WorkUnit fiveMinutes = new WorkUnit.Builder().setMinutes(5).build();
 
   TechnicalDebtCalculator remediationCostCalculator;
 
   @Before
   public void before() {
-    when(converter.toMinutes(tenMinutes)).thenReturn(10l);
-    when(converter.toMinutes(fiveMinutes)).thenReturn(5l);
+    Settings settings = new Settings();
+    settings.setProperty(CoreProperties.HOURS_IN_DAY, 8);
 
-    remediationCostCalculator = new TechnicalDebtCalculator(model, converter);
+    remediationCostCalculator = new TechnicalDebtCalculator(model, settings);
   }
 
   @Test
@@ -69,9 +68,7 @@ public class TechnicalDebtCalculatorTest {
     Mockito.when(requirement.offset()).thenReturn(fiveMinutes);
     when(model.requirementsByRule(ruleKey)).thenReturn(requirement);
 
-    remediationCostCalculator.calculTechnicalDebt(issue);
-
-    verify(converter).fromMinutes(10l + 5l);
+    assertThat(remediationCostCalculator.calculTechnicalDebt(issue)).isEqualTo(new WorkUnit.Builder().setMinutes(15).build());
   }
 
   @Test
@@ -85,9 +82,7 @@ public class TechnicalDebtCalculatorTest {
     Mockito.when(requirement.offset()).thenReturn(fiveMinutes);
     when(model.requirementsByRule(ruleKey)).thenReturn(requirement);
 
-    remediationCostCalculator.calculTechnicalDebt(issue);
-
-    verify(converter).fromMinutes(10l * 2 + 5l);
+    assertThat(remediationCostCalculator.calculTechnicalDebt(issue)).isEqualTo(new WorkUnit.Builder().setMinutes((10 * 2) + 5).build());
   }
 
   @Test
@@ -101,9 +96,7 @@ public class TechnicalDebtCalculatorTest {
     Mockito.when(requirement.offset()).thenReturn(null);
     when(model.requirementsByRule(ruleKey)).thenReturn(requirement);
 
-    remediationCostCalculator.calculTechnicalDebt(issue);
-
-    verify(converter).fromMinutes(10l * 2 + 0l);
+    assertThat(remediationCostCalculator.calculTechnicalDebt(issue)).isEqualTo(new WorkUnit.Builder().setMinutes(10 * 2).build());
   }
 
   @Test
@@ -117,9 +110,7 @@ public class TechnicalDebtCalculatorTest {
     Mockito.when(requirement.offset()).thenReturn(fiveMinutes);
     when(model.requirementsByRule(ruleKey)).thenReturn(requirement);
 
-    remediationCostCalculator.calculTechnicalDebt(issue);
-
-    verify(converter).fromMinutes(0l + 5l);
+    assertThat(remediationCostCalculator.calculTechnicalDebt(issue)).isEqualTo(new WorkUnit.Builder().setMinutes(5).build());
   }
 
   @Test
@@ -129,7 +120,6 @@ public class TechnicalDebtCalculatorTest {
     when(model.requirementsByRule(ruleKey)).thenReturn(null);
 
     assertThat(remediationCostCalculator.calculTechnicalDebt(issue)).isNull();
-    verify(converter, never()).fromMinutes(anyLong());
   }
 
   @Test
@@ -144,13 +134,12 @@ public class TechnicalDebtCalculatorTest {
     when(model.requirementsByRule(ruleKey)).thenReturn(requirement);
 
     try {
-      remediationCostCalculator.calculTechnicalDebt(issue);
+      assertThat(remediationCostCalculator.calculTechnicalDebt(issue)).isEqualTo(new WorkUnit.Builder().setMinutes(15).build());
       fail();
     } catch (Exception e) {
       assertThat(e).isInstanceOf(IllegalArgumentException.class)
         .hasMessage("Requirement for 'squid:AvoidCycle' can not use 'Constant/issue' remediation function because this rule does not have a fixed remediation cost.");
     }
-    verifyZeroInteractions(converter);
   }
 
 }
index cb1ecab4b7356542ff9bdda152653d17e1e67e27..b60bb58442646f4915368ef1bfc8056dd21ef908 100644 (file)
@@ -108,8 +108,8 @@ public class TechnicalDebtModelLoaderTest {
     DefaultRequirement requirement = result.requirementsByRule(ruleKey);
     assertThat(requirement.ruleKey()).isEqualTo(ruleKey);
     assertThat(requirement.function()).isEqualTo("linear");
-    assertThat(requirement.factor()).isEqualTo(WorkUnit.create(2d, WorkUnit.DAYS));
-    assertThat(requirement.offset()).isEqualTo(WorkUnit.create(0d, WorkUnit.DEFAULT_UNIT));
+    assertThat(requirement.factor()).isEqualTo(new WorkUnit.Builder().setDays(2).build());
+    assertThat(requirement.offset()).isEqualTo(new WorkUnit.Builder().setDays(0).build());
   }
 
 }
index 9cbd6c92045caade7ca1295fe195ed4770720cc0..538104db87cfa50e81ec8fc2a0ab49db9c182580 100644 (file)
@@ -28,8 +28,8 @@ import org.sonar.api.issue.ActionPlan;
 import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.issue.internal.DefaultIssueComment;
 import org.sonar.api.issue.internal.IssueChangeContext;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.user.User;
+import org.sonar.api.utils.WorkUnit;
 
 import javax.annotation.Nullable;
 
@@ -85,7 +85,7 @@ public class IssueUpdater implements BatchComponent, ServerComponent {
 
   public boolean assign(DefaultIssue issue, @Nullable User user, IssueChangeContext context) {
     String sanitizedAssignee = null;
-    if(user != null) {
+    if (user != null) {
       sanitizedAssignee = StringUtils.defaultIfBlank(user.login(), null);
     }
     if (!Objects.equal(sanitizedAssignee, issue.assignee())) {
@@ -200,8 +200,8 @@ public class IssueUpdater implements BatchComponent, ServerComponent {
     return setEffortToFix(issue, currentEffort, context);
   }
 
-  public boolean setTechnicalDebt(DefaultIssue issue, @Nullable WorkDayDuration value, IssueChangeContext context) {
-    WorkDayDuration oldValue = issue.technicalDebt();
+  public boolean setTechnicalDebt(DefaultIssue issue, @Nullable WorkUnit value, IssueChangeContext context) {
+    WorkUnit oldValue = issue.technicalDebt();
     if (!Objects.equal(value, oldValue)) {
       issue.setTechnicalDebt(value);
       issue.setFieldChange(context, TECHNICAL_DEBT, oldValue != null ? oldValue.toLong() : null, value != null ? value.toLong() : null);
@@ -212,8 +212,8 @@ public class IssueUpdater implements BatchComponent, ServerComponent {
     return false;
   }
 
-  public boolean setPastTechnicalDebt(DefaultIssue issue, @Nullable WorkDayDuration previousTechnicalDebt, IssueChangeContext context) {
-    WorkDayDuration currentTechnicalDebt = issue.technicalDebt();
+  public boolean setPastTechnicalDebt(DefaultIssue issue, @Nullable WorkUnit previousTechnicalDebt, IssueChangeContext context) {
+    WorkUnit currentTechnicalDebt = issue.technicalDebt();
     issue.setTechnicalDebt(previousTechnicalDebt);
     return setTechnicalDebt(issue, currentTechnicalDebt, context);
   }
@@ -232,7 +232,7 @@ public class IssueUpdater implements BatchComponent, ServerComponent {
 
   public boolean plan(DefaultIssue issue, @Nullable ActionPlan actionPlan, IssueChangeContext context) {
     String sanitizedActionPlanKey = null;
-    if(actionPlan != null) {
+    if (actionPlan != null) {
       sanitizedActionPlanKey = StringUtils.defaultIfBlank(actionPlan.key(), null);
     }
     if (!Objects.equal(sanitizedActionPlanKey, issue.actionPlanKey())) {
index 1574dcc9ff5c6cb8c22eae0ae114061fe704206d..3e34ad2c738888891bb2aef71bb238146670be00 100644 (file)
@@ -24,9 +24,9 @@ import com.google.common.base.Preconditions;
 import org.apache.commons.lang.builder.ToStringBuilder;
 import org.apache.commons.lang.builder.ToStringStyle;
 import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.api.utils.WorkUnit;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
@@ -422,7 +422,7 @@ public final class IssueDto implements Serializable {
     issue.setResolution(resolution);
     issue.setMessage(message);
     issue.setEffortToFix(effortToFix);
-    issue.setTechnicalDebt(technicalDebt != null ? WorkDayDuration.fromLong(technicalDebt) : null);
+    issue.setTechnicalDebt(technicalDebt != null ? WorkUnit.fromLong(technicalDebt) : null);
     issue.setLine(line);
     issue.setSeverity(severity);
     issue.setReporter(reporter);
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtConverter.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtConverter.java
deleted file mode 100644 (file)
index 7191034..0000000
+++ /dev/null
@@ -1,90 +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.technicaldebt;
-
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.BatchComponent;
-import org.sonar.api.ServerComponent;
-import org.sonar.api.config.Settings;
-import org.sonar.api.issue.internal.WorkDayDuration;
-import org.sonar.api.utils.WorkUnit;
-
-import javax.annotation.Nullable;
-
-public class TechnicalDebtConverter implements BatchComponent, ServerComponent {
-
-  public static final String PROPERTY_HOURS_IN_DAY = "sonar.technicalDebt.hoursInDay";
-
-  private int hoursInDay;
-
-  public TechnicalDebtConverter(Settings settings) {
-    this.hoursInDay = settings.getInt(PROPERTY_HOURS_IN_DAY);
-  }
-
-  public long toMinutes(WorkUnit factor) {
-    if (StringUtils.equals(WorkUnit.DAYS, factor.getUnit())) {
-      return Double.valueOf(factor.getValue() * hoursInDay * 60d).longValue();
-
-    } else if (StringUtils.equals(WorkUnit.HOURS, factor.getUnit())) {
-      return Double.valueOf(factor.getValue() * 60d).longValue();
-
-    } else if (StringUtils.equals(WorkUnit.MINUTES, factor.getUnit())) {
-      return Double.valueOf(factor.getValue()).longValue();
-
-    } else {
-      throw new IllegalArgumentException("Unknown remediation factor unit: " + factor.getUnit());
-    }
-  }
-
-  public double toDays(@Nullable WorkDayDuration technicalDebt) {
-    if (technicalDebt == null) {
-      return 0d;
-    }
-    double resultDays = technicalDebt.days();
-    resultDays += Double.valueOf(technicalDebt.hours()) / hoursInDay;
-    resultDays += Double.valueOf(technicalDebt.minutes()) / (hoursInDay * 60.0);
-    return resultDays;
-  }
-
-  public WorkDayDuration fromMinutes(Long inMinutes){
-    int oneHourInMinute = 60;
-    int days = 0;
-    int hours = 0;
-    int minutes = 0;
-
-    int oneWorkingDay = hoursInDay * oneHourInMinute;
-    if (inMinutes >= oneWorkingDay) {
-      Long nbDays = inMinutes / oneWorkingDay;
-      days = nbDays.shortValue();
-      inMinutes = inMinutes - (nbDays * oneWorkingDay);
-    }
-
-    if (inMinutes >= oneHourInMinute) {
-      Long nbHours = inMinutes / oneHourInMinute;
-      hours = nbHours.shortValue();
-      inMinutes = inMinutes - (nbHours * oneHourInMinute);
-    }
-
-    minutes = inMinutes.shortValue();
-
-    return WorkDayDuration.of(minutes, hours, days);
-  }
-
-}
index 0533e9c157bf8dc4db045cde9751ade55157b6c9..3a78a4430109a2cc6ae866318c1cb2a736f2bf63 100644 (file)
@@ -24,8 +24,8 @@ import org.sonar.api.issue.ActionPlan;
 import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.issue.internal.FieldDiffs;
 import org.sonar.api.issue.internal.IssueChangeContext;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.user.User;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.core.user.DefaultUser;
 
 import java.util.Date;
@@ -366,42 +366,42 @@ public class IssueUpdaterTest {
 
   @Test
   public void set_past_technical_debt() throws Exception {
-    issue.setTechnicalDebt(WorkDayDuration.of(15, 0, 0));
-    WorkDayDuration previousDebt = WorkDayDuration.of(10, 0, 0);
+    issue.setTechnicalDebt(new WorkUnit.Builder().setDays(15).build());
+    WorkUnit previousDebt = new WorkUnit.Builder().setDays(10).build();
     boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context);
     assertThat(updated).isTrue();
-    assertThat(issue.technicalDebt()).isEqualTo(WorkDayDuration.of(15, 0, 0));
+    assertThat(issue.technicalDebt()).isEqualTo(new WorkUnit.Builder().setDays(15).build());
     assertThat(issue.mustSendNotifications()).isFalse();
 
     FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT);
-    assertThat(diff.oldValue()).isEqualTo(WorkDayDuration.of(10, 0, 0).toLong());
-    assertThat(diff.newValue()).isEqualTo(WorkDayDuration.of(15, 0, 0).toLong());
+    assertThat(diff.oldValue()).isEqualTo(new WorkUnit.Builder().setDays(10).build().toLong());
+    assertThat(diff.newValue()).isEqualTo(new WorkUnit.Builder().setDays(15).build().toLong());
   }
 
   @Test
   public void set_past_technical_debt_without_previous_value() throws Exception {
-    issue.setTechnicalDebt(WorkDayDuration.of(15, 0, 0));
+    issue.setTechnicalDebt(new WorkUnit.Builder().setDays(15).build());
     boolean updated = updater.setPastTechnicalDebt(issue, null, context);
     assertThat(updated).isTrue();
-    assertThat(issue.technicalDebt()).isEqualTo(WorkDayDuration.of(15, 0, 0));
+    assertThat(issue.technicalDebt()).isEqualTo(new WorkUnit.Builder().setDays(15).build());
     assertThat(issue.mustSendNotifications()).isFalse();
 
     FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT);
     assertThat(diff.oldValue()).isNull();
-    assertThat(diff.newValue()).isEqualTo(WorkDayDuration.of(15, 0, 0).toLong());
+    assertThat(diff.newValue()).isEqualTo(new WorkUnit.Builder().setDays(15).build().toLong());
   }
 
   @Test
   public void set_past_technical_debt_with_null_new_value() throws Exception {
     issue.setTechnicalDebt(null);
-    WorkDayDuration previousDebt = WorkDayDuration.of(10, 0, 0);
+    WorkUnit previousDebt = new WorkUnit.Builder().setDays(10).build();
     boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context);
     assertThat(updated).isTrue();
     assertThat(issue.technicalDebt()).isNull();
     assertThat(issue.mustSendNotifications()).isFalse();
 
     FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT);
-    assertThat(diff.oldValue()).isEqualTo(WorkDayDuration.of(10, 0, 0).toLong());
+    assertThat(diff.oldValue()).isEqualTo(new WorkUnit.Builder().setDays(10).build().toLong());
     assertThat(diff.newValue()).isNull();
   }
 
index 484ad84f2a882316765f7891e64f7f27e6009772..e87ffb98153c3c6339795fee0b0b6401e34508f5 100644 (file)
@@ -25,7 +25,7 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.WorkDayDuration;
+import org.sonar.api.utils.WorkUnit;
 
 import java.util.Calendar;
 import java.util.Date;
@@ -56,28 +56,28 @@ public class IssueDtoTest {
     Date closedAt = DateUtils.addDays(new Date(), -1);
 
     IssueDto dto = new IssueDto()
-        .setKee("100")
-        .setRuleId(1)
-        .setRuleKey_unit_test_only("squid", "AvoidCycle")
-        .setComponentKey_unit_test_only("org.sonar.sample:Sample")
-        .setRootComponentKey_unit_test_only("org.sonar.sample")
-        .setComponentId(1l)
-        .setRootComponentId(1l)
-        .setStatus(Issue.STATUS_CLOSED)
-        .setResolution(Issue.RESOLUTION_FALSE_POSITIVE)
-        .setEffortToFix(15.0)
-        .setTechnicalDebt(101010L)
-        .setLine(6)
-        .setSeverity("BLOCKER")
-        .setMessage("message")
-        .setManualSeverity(true)
-        .setReporter("arthur")
-        .setAssignee("perceval")
-        .setIssueAttributes("key=value")
-        .setAuthorLogin("pierre")
-        .setIssueCreationDate(createdAt)
-        .setIssueUpdateDate(updatedAt)
-        .setIssueCloseDate(closedAt);
+      .setKee("100")
+      .setRuleId(1)
+      .setRuleKey_unit_test_only("squid", "AvoidCycle")
+      .setComponentKey_unit_test_only("org.sonar.sample:Sample")
+      .setRootComponentKey_unit_test_only("org.sonar.sample")
+      .setComponentId(1l)
+      .setRootComponentId(1l)
+      .setStatus(Issue.STATUS_CLOSED)
+      .setResolution(Issue.RESOLUTION_FALSE_POSITIVE)
+      .setEffortToFix(15.0)
+      .setTechnicalDebt(101010L)
+      .setLine(6)
+      .setSeverity("BLOCKER")
+      .setMessage("message")
+      .setManualSeverity(true)
+      .setReporter("arthur")
+      .setAssignee("perceval")
+      .setIssueAttributes("key=value")
+      .setAuthorLogin("pierre")
+      .setIssueCreationDate(createdAt)
+      .setIssueUpdateDate(updatedAt)
+      .setIssueCloseDate(closedAt);
 
     DefaultIssue issue = dto.toDefaultIssue();
     assertThat(issue.key()).isEqualTo("100");
@@ -87,7 +87,7 @@ public class IssueDtoTest {
     assertThat(issue.status()).isEqualTo(Issue.STATUS_CLOSED);
     assertThat(issue.resolution()).isEqualTo(Issue.RESOLUTION_FALSE_POSITIVE);
     assertThat(issue.effortToFix()).isEqualTo(15.0);
-    assertThat(issue.technicalDebt()).isEqualTo(WorkDayDuration.of(10, 10, 10));
+    assertThat(issue.technicalDebt()).isEqualTo(new WorkUnit.Builder().setDays(10).setHours(10).setMinutes(10).build());
     assertThat(issue.line()).isEqualTo(6);
     assertThat(issue.severity()).isEqualTo("BLOCKER");
     assertThat(issue.message()).isEqualTo("message");
index 1c6281c837a005aba9b7ecffb93ca663553f38aa..5c8b3487f8ef11044a38626e57dfb216eef66719 100644 (file)
@@ -23,12 +23,12 @@ import org.junit.Test;
 import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.issue.internal.DefaultIssueComment;
 import org.sonar.api.issue.internal.IssueChangeContext;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.rules.RuleFinder;
 import org.sonar.api.rules.RuleQuery;
 import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.core.persistence.AbstractDaoTestCase;
 import org.sonar.core.persistence.MyBatis;
 
@@ -54,7 +54,7 @@ public class IssueStorageTest extends AbstractDaoTestCase {
 
       .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
       .setLine(5000)
-      .setTechnicalDebt(WorkDayDuration.of(10, 0, 0))
+      .setTechnicalDebt(new WorkUnit.Builder().setMinutes(10).build())
       .setReporter("emmerik")
       .setResolution("OPEN")
       .setStatus("OPEN")
@@ -88,7 +88,7 @@ public class IssueStorageTest extends AbstractDaoTestCase {
 
         // updated fields
       .setLine(5000)
-      .setTechnicalDebt(WorkDayDuration.of(10, 0, 0))
+      .setTechnicalDebt(new WorkUnit.Builder().setMinutes(10).build())
       .setChecksum("FFFFF")
       .setAuthorLogin("simon")
       .setAssignee("loic")
index a69517c7c9951f75e0b17acca2769762fc5658b3..69877f818984fd71ace6a9d186013878406d5b2c 100644 (file)
@@ -89,8 +89,8 @@ public class DefaultTechnicalDebtManagerTest {
     assertThat(result.rootId()).isEqualTo(1);
     assertThat(result.ruleKey()).isEqualTo(RuleKey.of("repo", "key"));
     assertThat(result.function()).isEqualTo("linear");
-    assertThat(result.factor()).isEqualTo(WorkUnit.create(30.0, WorkUnit.MINUTES));
-    assertThat(result.offset()).isEqualTo(WorkUnit.create());
+    assertThat(result.factor()).isEqualTo(new WorkUnit.Builder().setMinutes(30).build());
+    assertThat(result.offset()).isEqualTo(new WorkUnit.Builder().setDays(0).build());
   }
 
   @Test
@@ -149,8 +149,8 @@ public class DefaultTechnicalDebtManagerTest {
     assertThat(result.rootId()).isEqualTo(1);
     assertThat(result.ruleKey()).isEqualTo(RuleKey.of("repo", "key"));
     assertThat(result.function()).isEqualTo("linear");
-    assertThat(result.factor()).isEqualTo(WorkUnit.create(30.0, WorkUnit.MINUTES));
-    assertThat(result.offset()).isEqualTo(WorkUnit.create());
+    assertThat(result.factor()).isEqualTo(new WorkUnit.Builder().setMinutes(30).build());
+    assertThat(result.offset()).isEqualTo(new WorkUnit.Builder().setDays(0).build());
   }
 
   @Test
index dcf61654fc317a882dc706c60189ae308dd1ab86..4177c144c6440f5a1af5fa9a9e9391ef144764e2 100644 (file)
@@ -92,8 +92,8 @@ public class DefaultTechnicalDebtModelTest {
       .setCharacteristic(characteristic)
       .setRuleKey(ruleKey)
       .setFunction("linear")
-      .setFactor(WorkUnit.create(2d, WorkUnit.HOURS))
-      .setOffset(WorkUnit.create(0d, WorkUnit.HOURS));
+      .setFactor(new WorkUnit.Builder().setHours(2).build())
+      .setOffset(new WorkUnit.Builder().setHours(0).build());
 
     sqaleModel.addRootCharacteristic(rootCharacteristic);
 
diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtConverterTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtConverterTest.java
deleted file mode 100644 (file)
index c196320..0000000
+++ /dev/null
@@ -1,85 +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.technicaldebt;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.config.Settings;
-import org.sonar.api.issue.internal.WorkDayDuration;
-import org.sonar.api.utils.WorkUnit;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-
-public class TechnicalDebtConverterTest {
-
-  private TechnicalDebtConverter converter;
-
-  @Before
-  public void before(){
-    Settings settings = new Settings();
-    settings.setProperty(TechnicalDebtConverter.PROPERTY_HOURS_IN_DAY, "12");
-
-    converter = new TechnicalDebtConverter(settings);
-  }
-
-  @Test
-  public void concert_work_unit_to_minutes() {
-    assertThat(converter.toMinutes(WorkUnit.create(2.0, WorkUnit.DAYS))).isEqualTo(2 * 12 * 60L);
-    assertThat(converter.toMinutes(WorkUnit.create(6.0, WorkUnit.HOURS))).isEqualTo(6 * 60L);
-    assertThat(converter.toMinutes(WorkUnit.create(60.0, WorkUnit.MINUTES))).isEqualTo(60L);
-  }
-
-  @Test
-  public void convert_simple_values() {
-    checkValues(converter.fromMinutes(15L), 15, 0, 0);
-    checkValues(converter.fromMinutes(120L), 0, 2, 0);
-    checkValues(converter.fromMinutes(720L), 0, 0, 1);
-  }
-
-  @Test
-  public void convert_complex_values() {
-    checkValues(converter.fromMinutes(70L), 10, 1, 0);
-    checkValues(converter.fromMinutes(730L), 10, 0, 1);
-    checkValues(converter.fromMinutes(790L), 10, 1, 1);
-  }
-
-  @Test
-  public void convert_technical_debt_to_days() {
-    assertThat(converter.toDays(WorkDayDuration.of(0, 0, 6))).isEqualTo(6.0);
-    assertThat(converter.toDays(WorkDayDuration.of(0, 6, 0))).isEqualTo(0.5);
-    assertThat(converter.toDays(WorkDayDuration.of(360, 0, 0))).isEqualTo(0.5);
-    assertThat(converter.toDays(WorkDayDuration.of(45, 0, 0))).isEqualTo(0.0625);
-
-    assertThat(converter.toDays(WorkDayDuration.of(45, 6, 1))).isEqualTo(1.5625);
-  }
-
-  @Test
-  public void return_zero_if_null_when_convert_technical_debt_to_days() {
-    assertThat(converter.toDays((WorkDayDuration) null)).isEqualTo(0.0);
-  }
-  
-  private void checkValues(WorkDayDuration technicalDebt, int expectedMinutes, int expectedHours, int expectedDays) {
-    assertThat(technicalDebt.minutes()).isEqualTo(expectedMinutes);
-    assertThat(technicalDebt.hours()).isEqualTo(expectedHours);
-    assertThat(technicalDebt.days()).isEqualTo(expectedDays);
-  }
-
-}
index e037a48280fff9472c3e8ffeb8dd547e1853517e..17e4bee2e8aeaa510ea0e46e065fa3c8a73be9f0 100644 (file)
@@ -142,7 +142,7 @@ public class TechnicalDebtModelSynchronizerTest {
     RuleKey ruleKey = RuleKey.of("checkstyle", "import");
     when(ruleCache.getByRuleKey(ruleKey)).thenReturn(rule);
     new DefaultRequirement().setRuleKey(ruleKey)
-      .setFunction("linear").setFactor(WorkUnit.create(30.0, WorkUnit.MINUTES)).setCharacteristic(javaCharacteristic).setRootCharacteristic(javaRootCharacteristic);
+      .setFunction("linear").setFactor(new WorkUnit.Builder().setMinutes(30).build()).setCharacteristic(javaCharacteristic).setRootCharacteristic(javaRootCharacteristic);
 
     Reader javaModelReader = mock(Reader.class);
     when(xmlImporter.importXML(eq(javaModelReader), any(ValidationMessages.class), eq(ruleCache))).thenReturn(javaModel);
@@ -195,7 +195,7 @@ public class TechnicalDebtModelSynchronizerTest {
 
     // New requirement
     new DefaultRequirement().setRuleKey(ruleKey2)
-      .setFunction("linear").setFactor(WorkUnit.create(1.0, WorkUnit.HOURS)).setCharacteristic(javaCharacteristic).setRootCharacteristic(javaRootCharacteristic);
+      .setFunction("linear").setFactor(new WorkUnit.Builder().setHours(1).build()).setCharacteristic(javaCharacteristic).setRootCharacteristic(javaRootCharacteristic);
 
     Reader javaModelReader = mock(Reader.class);
     when(technicalDebtModelRepository.createReaderForXMLFile("java")).thenReturn(javaModelReader);
index 1670422d435ec472803e69efa5b41099ec3b8e53..d614d7588a0d9a2070a7fd09da5ba3b16223b51d 100644 (file)
@@ -189,7 +189,7 @@ public class TechnicalDebtXMLImporterTest {
   }
 
   private void checkXmlCorrectlyImported(DefaultTechnicalDebtModel sqale, ValidationMessages messages) {
-    checkXmlCorrectlyImported(sqale, WorkUnit.create(), messages);
+    checkXmlCorrectlyImported(sqale, new WorkUnit.Builder().setDays(0).build(), messages);
   }
 
   private void checkXmlCorrectlyImported(DefaultTechnicalDebtModel sqale, WorkUnit offset, ValidationMessages messages) {
index d0a38dbdc84ca64cd8ec4f860ce10810e5e282c5..5e0795830d02850e1d2b4ee35c6127024188f684 100644 (file)
@@ -543,4 +543,9 @@ public interface CoreProperties {
    * @since 4.2
    */
   String CORE_AUTHENTICATOR_LOCAL_USERS = "sonar.security.localUsers";
+
+  /**
+   * @since 4.0
+   */
+  String HOURS_IN_DAY = "sonar.technicalDebt.hoursInDay";
 }
index 101550769637088da8f2651aa6e0bf0a164678b6..dcb7e1a7bebca34476afdb20be5ea52a8d3d8032 100644 (file)
@@ -33,6 +33,7 @@ import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.IssueComment;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
+import org.sonar.api.utils.WorkUnit;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
@@ -58,7 +59,7 @@ public class DefaultIssue implements Issue {
   private String message;
   private Integer line;
   private Double effortToFix;
-  private WorkDayDuration technicalDebt;
+  private WorkUnit technicalDebt;
   private String status;
   private String resolution;
   private String reporter;
@@ -195,11 +196,11 @@ public class DefaultIssue implements Issue {
    * Elapsed time to fix the issue
    */
   @CheckForNull
-  public WorkDayDuration technicalDebt() {
+  public WorkUnit technicalDebt() {
     return technicalDebt;
   }
 
-  public DefaultIssue setTechnicalDebt(@Nullable WorkDayDuration t) {
+  public DefaultIssue setTechnicalDebt(@Nullable WorkUnit t) {
     this.technicalDebt = t;
     return this;
   }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/WorkDayDuration.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/WorkDayDuration.java
deleted file mode 100644 (file)
index a2acecf..0000000
+++ /dev/null
@@ -1,113 +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.issue.internal;
-
-import java.io.Serializable;
-
-public class WorkDayDuration implements Serializable {
-
-  private static final int DAY = 10000;
-  private static final int HOUR = 100;
-  private static final int MINUTE = 1;
-
-  private int days;
-  private int hours;
-  private int minutes;
-
-  private WorkDayDuration(int minutes, int hours, int days) {
-    this.minutes = minutes;
-    this.hours = hours;
-    this.days = days;
-  }
-
-  private WorkDayDuration(long durationInLong) {
-    long time = durationInLong;
-    Long currentTime = time / DAY;
-    if (currentTime > 0) {
-      this.days = currentTime.intValue();
-      time = time - (currentTime * DAY);
-    }
-
-    currentTime = time / HOUR;
-    if (currentTime > 0) {
-      this.hours = currentTime.intValue();
-      time = time - (currentTime * HOUR);
-    }
-
-    currentTime = time / MINUTE;
-    if (currentTime > 0) {
-      this.minutes = currentTime.intValue();
-    }
-  }
-
-  public static WorkDayDuration of(int minutes, int hours, int days) {
-    return new WorkDayDuration(minutes, hours, days);
-  }
-
-  public static WorkDayDuration fromLong(long durationInLong) {
-    return new WorkDayDuration(durationInLong);
-  }
-
-  public long toLong() {
-    return days * DAY + hours * HOUR + minutes * MINUTE;
-  }
-
-  public int days() {
-    return days;
-  }
-
-  public int hours() {
-    return hours;
-  }
-
-  public int minutes() {
-    return minutes;
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
-      return true;
-    }
-    if (o == null || getClass() != o.getClass()) {
-      return false;
-    }
-    WorkDayDuration workDayDuration = (WorkDayDuration) o;
-    if (days != workDayDuration.days) {
-      return false;
-    }
-    if (hours != workDayDuration.hours) {
-      return false;
-    }
-    if (minutes != workDayDuration.minutes) {
-      return false;
-    }
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    int result = Integer.valueOf(days).hashCode();
-    result = 29 * result + Integer.valueOf(hours).hashCode();
-    result = 27 * result + Integer.valueOf(minutes).hashCode();
-    return result;
-  }
-
-}
index 66f4778c5e4196c74bd4939d4a4c27d20d894c1a..8f80e3fc8ecc48383ea266924a50e9a4e8bc4a27 100644 (file)
@@ -47,8 +47,8 @@ public class DefaultRequirement implements Requirement {
   private Date updatedAt;
 
   public DefaultRequirement() {
-    this.factor = WorkUnit.create(0d, WorkUnit.DEFAULT_UNIT);
-    this.offset = WorkUnit.create(0d, WorkUnit.DEFAULT_UNIT);
+    this.factor = new WorkUnit.Builder().setDays(0).build();
+    this.offset = new WorkUnit.Builder().setDays(0).build();
   }
 
   public Integer id() {
index 61a5aeec2a7f4c9356210a0099dd483dadd761cc..2686663765c3530d42665cf42faf3492c1c9d14b 100644 (file)
@@ -26,30 +26,42 @@ import org.apache.commons.lang.builder.ToStringStyle;
 
 import javax.annotation.Nullable;
 
-public final class WorkUnit {
+import java.io.Serializable;
+
+/**
+ * @since 4.0
+ */
+public final class WorkUnit implements Serializable {
 
   public static final String DAYS = "d";
   public static final String MINUTES = "mn";
   public static final String HOURS = "h";
   public static final String DEFAULT_UNIT = DAYS;
-  private static final String[] UNITS = {DAYS, MINUTES, HOURS};
 
   public static final double DEFAULT_VALUE = 0.0;
 
-  private double value = 0d;
-  private String unit = DEFAULT_UNIT;
+  private static final String[] UNITS = {DAYS, MINUTES, HOURS};
 
-  WorkUnit(double value, String unit) {
-    this.value = value;
-    this.unit = unit;
-  }
+  private static final int DAY = 10000;
+  private static final int HOUR = 100;
+  private static final int MINUTE = 1;
 
-  public double getValue() {
-    return value;
+  private int days;
+  private int hours;
+  private int minutes;
+
+  private WorkUnit(int days, int hours, int minutes) {
+    this.minutes = minutes;
+    this.hours = hours;
+    this.days = days;
   }
 
-  public String getUnit() {
-    return unit;
+  /**
+   * @deprecated since 4.2.
+   */
+  @Deprecated
+  public static WorkUnit create() {
+    return create(0d, DEFAULT_UNIT);
   }
 
   public static WorkUnit create(@Nullable Double value, @Nullable String unit) {
@@ -57,15 +69,114 @@ public final class WorkUnit {
     if (!ArrayUtils.contains(UNITS, defaultIfEmptyUnit)) {
       throw new IllegalArgumentException("Unit can not be: " + defaultIfEmptyUnit + ". Possible values are " + ArrayUtils.toString(UNITS));
     }
-    double d = value != null ? value : DEFAULT_VALUE;
+    Double d = value != null ? value : DEFAULT_VALUE;
     if (d < 0.0) {
       throw new IllegalArgumentException("Value can not be negative: " + d);
     }
-    return new WorkUnit(d, defaultIfEmptyUnit);
+
+    int days = 0;
+    int hours = 0;
+    int minutes = 0;
+    if (DAYS.equals(unit)) {
+      days = d.intValue();
+    } else if (HOURS.equals(unit)) {
+      hours = d.intValue();
+    } else if (MINUTES.equals(unit)) {
+      minutes = d.intValue();
+    }
+    return new WorkUnit(days, hours, minutes);
   }
 
-  public static WorkUnit create() {
-    return create(0d, DEFAULT_UNIT);
+  public double getValue() {
+    if (days > 0) {
+      return days + (hours / 24) + (minutes / 60 / 24);
+    } else if (hours > 0) {
+      return hours + (minutes / 60);
+    } else {
+      return minutes;
+    }
+  }
+
+  public String getUnit() {
+    if (days > 0) {
+      return DAYS;
+    } else if (hours > 0) {
+      return HOURS;
+    } else {
+      return MINUTES;
+    }
+  }
+
+  /**
+   * @since 4.2
+   */
+  public int days() {
+    return days;
+  }
+
+  /**
+   * @since 4.2
+   */
+  public int hours() {
+    return hours;
+  }
+
+  /**
+   * @since 4.2
+   */
+  public int minutes() {
+    return minutes;
+  }
+
+  /**
+   *
+   * @since 4.2
+   */
+  public static WorkUnit fromLong(long durationInLong) {
+    Builder builder = new Builder();
+
+    long time = durationInLong;
+    Long currentTime = time / DAY;
+    if (currentTime > 0) {
+      builder.setDays(currentTime.intValue());
+      time = time - (currentTime * DAY);
+    }
+
+    currentTime = time / HOUR;
+    if (currentTime > 0) {
+      builder.setHours(currentTime.intValue());
+      time = time - (currentTime * HOUR);
+    }
+
+    currentTime = time / MINUTE;
+    if (currentTime > 0) {
+      builder.setMinutes(currentTime.intValue());
+    }
+
+    return builder.build();
+  }
+
+  /**
+   * Return the duration using the following format DDHHMM, where DD is the number of days, HH is the number of months, and MM the number of minutes.
+   * For instance, 5 days and 2 hours will return 050200.
+   *
+   * @since 4.2
+   */
+  public long toLong() {
+    return days * DAY + hours * HOUR + minutes * MINUTE;
+  }
+
+  /**
+   * Return the duration in number of days.
+   * For instance, 5 days and 4 hours will return 5.5 hours (if hoursIndDay is 8).
+   *
+   * @since 4.2
+   */
+  public double toDays(int hoursInDay) {
+    double resultDays = days;
+    resultDays += (double) hours / hoursInDay;
+    resultDays += (double) minutes / (hoursInDay * 60.0);
+    return resultDays;
   }
 
   @Override
@@ -76,26 +187,24 @@ public final class WorkUnit {
     if (o == null || getClass() != o.getClass()) {
       return false;
     }
-
-    WorkUnit workUnit = (WorkUnit) o;
-
-    if (Double.compare(workUnit.value, value) != 0) {
+    WorkUnit workDayDuration = (WorkUnit) o;
+    if (days != workDayDuration.days) {
       return false;
     }
-    if (!unit.equals(workUnit.unit)) {
+    if (hours != workDayDuration.hours) {
+      return false;
+    }
+    if (minutes != workDayDuration.minutes) {
       return false;
     }
-
     return true;
   }
 
   @Override
   public int hashCode() {
-    int result;
-    long temp;
-    temp = Double.doubleToLongBits(value);
-    result = (int) (temp ^ (temp >>> 32));
-    result = 31 * result + unit.hashCode();
+    int result = Integer.valueOf(days).hashCode();
+    result = 29 * result + Integer.valueOf(hours).hashCode();
+    result = 27 * result + Integer.valueOf(minutes).hashCode();
     return result;
   }
 
@@ -103,4 +212,32 @@ public final class WorkUnit {
   public String toString() {
     return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
   }
+
+  /**
+   * @since 4.2
+   */
+  public static class Builder {
+    private int days;
+    private int hours;
+    private int minutes;
+
+    public Builder setDays(int days) {
+      this.days = days;
+      return this;
+    }
+
+    public Builder setHours(int hours) {
+      this.hours = hours;
+      return this;
+    }
+
+    public Builder setMinutes(int minutes) {
+      this.minutes = minutes;
+      return this;
+    }
+
+    public WorkUnit build() {
+      return new WorkUnit(days, hours, minutes);
+    }
+  }
 }
index 0e464d40ec5940940fc544a339d0f26aac0b2d82..1b385766ac42c1df035258b866a61d797979a7b8 100644 (file)
@@ -25,6 +25,7 @@ import org.junit.Test;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.IssueComment;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.WorkUnit;
 
 import java.text.SimpleDateFormat;
 import java.util.List;
@@ -49,7 +50,7 @@ public class DefaultIssueTest {
       .setMessage("a message")
       .setLine(7)
       .setEffortToFix(1.2d)
-      .setTechnicalDebt(WorkDayDuration.of(1, 0, 0))
+      .setTechnicalDebt(new WorkUnit.Builder().setDays(1).build())
       .setActionPlanKey("BCDE")
       .setStatus(Issue.STATUS_CLOSED)
       .setResolution(Issue.RESOLUTION_FIXED)
@@ -77,7 +78,7 @@ public class DefaultIssueTest {
     assertThat(issue.message()).isEqualTo("a message");
     assertThat(issue.line()).isEqualTo(7);
     assertThat(issue.effortToFix()).isEqualTo(1.2d);
-    assertThat(issue.technicalDebt()).isEqualTo(WorkDayDuration.of(1, 0, 0));
+    assertThat(issue.technicalDebt()).isEqualTo(new WorkUnit.Builder().setDays(1).build());
     assertThat(issue.actionPlanKey()).isEqualTo("BCDE");
     assertThat(issue.status()).isEqualTo(Issue.STATUS_CLOSED);
     assertThat(issue.resolution()).isEqualTo(Issue.RESOLUTION_FIXED);
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/WorkDayDurationTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/WorkDayDurationTest.java
deleted file mode 100644 (file)
index ccff88c..0000000
+++ /dev/null
@@ -1,73 +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.issue.internal;
-
-import org.junit.Test;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class WorkDayDurationTest {
-
-  @Test
-  public void from_long_on_simple_values(){
-    checkTimes(WorkDayDuration.fromLong(1L), 0, 0, 1);
-    checkTimes(WorkDayDuration.fromLong(100L), 0, 1, 0);
-    checkTimes(WorkDayDuration.fromLong(10000L), 1, 0, 0);
-  }
-
-  @Test
-  public void from_long_on_complex_values(){
-    checkTimes(WorkDayDuration.fromLong(10101L), 1, 1, 1);
-    checkTimes(WorkDayDuration.fromLong(101L), 0, 1, 1);
-    checkTimes(WorkDayDuration.fromLong(10001L), 1, 0, 1);
-    checkTimes(WorkDayDuration.fromLong(10100L), 1, 1, 0);
-
-    checkTimes(WorkDayDuration.fromLong(112233L), 11, 22, 33);
-  }
-
-  @Test
-  public void to_long(){
-    assertThat(WorkDayDuration.of(1, 1, 1).toLong()).isEqualTo(10101L);
-  }
-
-  @Test
-  public void test_equals_and_hashCode() throws Exception {
-    WorkDayDuration oneMinute = WorkDayDuration.fromLong(1L);
-    WorkDayDuration oneHours = WorkDayDuration.fromLong(100L);
-    WorkDayDuration oneDay = WorkDayDuration.fromLong(10000L);
-
-    assertThat(oneMinute).isEqualTo(oneMinute);
-    assertThat(oneMinute).isEqualTo(WorkDayDuration.fromLong(1L));
-    assertThat(oneHours).isEqualTo(WorkDayDuration.fromLong(100L));
-    assertThat(oneDay).isEqualTo(WorkDayDuration.fromLong(10000L));
-
-    assertThat(oneMinute).isNotEqualTo(oneHours);
-    assertThat(oneHours).isNotEqualTo(oneDay);
-
-    assertThat(oneMinute.hashCode()).isEqualTo(oneMinute.hashCode());
-  }
-
-  private void checkTimes(WorkDayDuration technicalDebt, int expectedDays, int expectedHours, int expectedMinutes){
-    assertThat(technicalDebt.days()).isEqualTo(expectedDays);
-    assertThat(technicalDebt.hours()).isEqualTo(expectedHours);
-    assertThat(technicalDebt.minutes()).isEqualTo(expectedMinutes);
-  }
-
-}
index 3c8c246165fb5be3cbef8a31d46f30f6b9ac1533..f3bda18103803b3b5d6d7281af64c3180ae6a3a1 100644 (file)
@@ -47,8 +47,8 @@ public class DefaultRequirementTest {
       .setCharacteristic(characteristic)
       .setRootCharacteristic(root)
       .setFunction("linear_offset")
-      .setFactor(WorkUnit.create(2.0, "mn"))
-      .setOffset(WorkUnit.create(1.0, "h"))
+      .setFactor(new WorkUnit.Builder().setMinutes(2).build())
+      .setOffset(new WorkUnit.Builder().setHours(1).build())
       .setCreatedAt(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19"))
       .setUpdatedAt(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19"));
 
@@ -57,8 +57,8 @@ public class DefaultRequirementTest {
     assertThat(requirement.characteristic()).isEqualTo(characteristic);
     assertThat(requirement.rootCharacteristic()).isEqualTo(root);
     assertThat(requirement.function()).isEqualTo("linear_offset");
-    assertThat(requirement.factor()).isEqualTo(WorkUnit.create(2.0, "mn"));
-    assertThat(requirement.offset()).isEqualTo(WorkUnit.create(1.0, "h"));
+    assertThat(requirement.factor()).isEqualTo(new WorkUnit.Builder().setMinutes(2).build());
+    assertThat(requirement.offset()).isEqualTo(new WorkUnit.Builder().setHours(1).build());
     assertThat(requirement.createdAt()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19"));
     assertThat(requirement.updatedAt()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19"));
   }
index 3217dde1a7b0826dc41ddbbaf3742698a94d531c..72835e0705ba5b604cad22a2d24ef3830f70cf07 100644 (file)
@@ -56,8 +56,8 @@ public class DefaultCharacteristicTest {
       .setId(1)
       .setRuleKey(RuleKey.of("repo", "rule"))
       .setFunction("linear_offset")
-      .setFactor(WorkUnit.create(2.0, "mn"))
-      .setOffset(WorkUnit.create(1.0, "h"))
+      .setFactor(new WorkUnit.Builder().setMinutes(2).build())
+      .setOffset(new WorkUnit.Builder().setHours(1).build())
       .setRootId(3)
       .setParentId(2);
 
@@ -67,8 +67,8 @@ public class DefaultCharacteristicTest {
     assertThat(requirement.order()).isNull();
     assertThat(requirement.ruleKey()).isEqualTo(RuleKey.of("repo", "rule"));
     assertThat(requirement.function()).isEqualTo("linear_offset");
-    assertThat(requirement.factor()).isEqualTo(WorkUnit.create(2.0, "mn"));
-    assertThat(requirement.offset()).isEqualTo(WorkUnit.create(1.0, "h"));
+    assertThat(requirement.factor()).isEqualTo(new WorkUnit.Builder().setMinutes(2).build());
+    assertThat(requirement.offset()).isEqualTo(new WorkUnit.Builder().setHours(1).build());
     assertThat(requirement.parentId()).isEqualTo(2);
     assertThat(requirement.rootId()).isEqualTo(3);
   }
@@ -92,8 +92,8 @@ public class DefaultCharacteristicTest {
       .setId(1)
       .setRuleKey(RuleKey.of("repo", "rule"))
       .setFunction("linear_offset")
-      .setFactor(WorkUnit.create(2.0, "mn"))
-      .setOffset(WorkUnit.create(1.0, "h"))
+      .setFactor(new WorkUnit.Builder().setMinutes(2).build())
+      .setOffset(new WorkUnit.Builder().setHours(1).build())
       .setRootId(3)
       .setParentId(2);
 
index 108f7aa57c277bdf0da5e36645c3f61b8bc880d5..c77e0b8bbf6e440198904e75b7a01c314ffba2cf 100644 (file)
@@ -36,17 +36,9 @@ public class WorkUnitTest {
   @Test
   public void create_default() throws Exception {
     WorkUnit workUnit = WorkUnit.create();
-    assertThat(workUnit.getUnit()).isEqualTo("d");
     assertThat(workUnit.getValue()).isEqualTo(0.0);
   }
 
-  @Test
-  public void test_equals() throws Exception {
-    assertThat(WorkUnit.create(2.0, "mn")).isEqualTo(WorkUnit.create(2.0, "mn"));
-    assertThat(WorkUnit.create(3.0, "mn")).isNotEqualTo(WorkUnit.create(2.0, "mn"));
-    assertThat(WorkUnit.create(2.0, "h")).isNotEqualTo(WorkUnit.create(2.0, "mn"));
-  }
-
   @Test
   public void fail_with_bad_unit() throws Exception {
     try {
@@ -65,4 +57,49 @@ public class WorkUnitTest {
     }
   }
 
+  @Test
+  public void from_long_on_simple_values() {
+    checkTimes(WorkUnit.fromLong(1L), 0, 0, 1);
+    checkTimes(WorkUnit.fromLong(100L), 0, 1, 0);
+    checkTimes(WorkUnit.fromLong(10000L), 1, 0, 0);
+  }
+
+  @Test
+  public void from_long_on_complex_values() {
+    checkTimes(WorkUnit.fromLong(10101L), 1, 1, 1);
+    checkTimes(WorkUnit.fromLong(101L), 0, 1, 1);
+    checkTimes(WorkUnit.fromLong(10001L), 1, 0, 1);
+    checkTimes(WorkUnit.fromLong(10100L), 1, 1, 0);
+
+    checkTimes(WorkUnit.fromLong(112233L), 11, 22, 33);
+  }
+
+  @Test
+  public void to_long() {
+    assertThat(new WorkUnit.Builder().setDays(1).setHours(1).setMinutes(1).build().toLong()).isEqualTo(10101L);
+  }
+
+  @Test
+  public void test_equals_and_hashCode() throws Exception {
+    WorkUnit oneMinute = WorkUnit.fromLong(1L);
+    WorkUnit oneHours = WorkUnit.fromLong(100L);
+    WorkUnit oneDay = WorkUnit.fromLong(10000L);
+
+    assertThat(oneMinute).isEqualTo(oneMinute);
+    assertThat(oneMinute).isEqualTo(WorkUnit.fromLong(1L));
+    assertThat(oneHours).isEqualTo(WorkUnit.fromLong(100L));
+    assertThat(oneDay).isEqualTo(WorkUnit.fromLong(10000L));
+
+    assertThat(oneMinute).isNotEqualTo(oneHours);
+    assertThat(oneHours).isNotEqualTo(oneDay);
+
+    assertThat(oneMinute.hashCode()).isEqualTo(oneMinute.hashCode());
+  }
+
+  private void checkTimes(WorkUnit technicalDebt, int expectedDays, int expectedHours, int expectedMinutes) {
+    assertThat(technicalDebt.days()).isEqualTo(expectedDays);
+    assertThat(technicalDebt.hours()).isEqualTo(expectedHours);
+    assertThat(technicalDebt.minutes()).isEqualTo(expectedMinutes);
+  }
+
 }
index 56d38affc159080f4e20fa373b01c9d170bde21f..d92ea0f1ceea5f75e3c708baf34223012c0cd842 100644 (file)
@@ -21,7 +21,7 @@ package org.sonar.server.issue;
 
 import org.sonar.api.ServerComponent;
 import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.api.issue.internal.WorkDayDuration;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.core.i18n.DefaultI18n;
 import org.sonar.core.issue.IssueUpdater;
 import org.sonar.server.technicaldebt.DebtFormatter;
@@ -74,10 +74,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 = debtFormatter.format(locale, WorkDayDuration.fromLong(Long.parseLong(newValueString)));
+        newValueString = debtFormatter.format(locale, WorkUnit.fromLong(Long.parseLong(newValueString)));
       }
       if (oldValueString != null) {
-        oldValueString = debtFormatter.format(locale, WorkDayDuration.fromLong(Long.parseLong(oldValueString)));
+        oldValueString = debtFormatter.format(locale, WorkUnit.fromLong(Long.parseLong(oldValueString)));
       }
     }
     return new IssueChangelogDiffFormat(oldValueString, newValueString);
index 856a4a38c514b76395d4669a4d97acca89e0ba88..096ca6e225524eea6e6b285e6573af9d4cb7998e 100644 (file)
@@ -25,13 +25,13 @@ import org.sonar.api.issue.*;
 import org.sonar.api.issue.action.Action;
 import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.RequestHandler;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.technicaldebt.server.Characteristic;
 import org.sonar.api.user.User;
 import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.workflow.Transition;
@@ -47,6 +47,7 @@ import org.sonar.server.user.UserSession;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
+
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
@@ -100,7 +101,7 @@ public class IssueShowWsHandler implements RequestHandler {
     Component project = result.project(issue);
     String actionPlanKey = issue.actionPlanKey();
     ActionPlan actionPlan = result.actionPlan(issue);
-    WorkDayDuration technicalDebt = issue.technicalDebt();
+    WorkUnit technicalDebt = issue.technicalDebt();
     Date updateDate = issue.updateDate();
     Date closeDate = issue.closeDate();
 
@@ -114,7 +115,7 @@ public class IssueShowWsHandler implements RequestHandler {
       .prop("rule", issue.ruleKey().toString())
       .prop("ruleName", result.rule(issue).getName())
       .prop("line", issue.line())
-      .prop("message",issue.message())
+      .prop("message", issue.message())
       .prop("resolution", issue.resolution())
       .prop("status", issue.status())
       .prop("severity", issue.severity())
@@ -147,7 +148,7 @@ public class IssueShowWsHandler implements RequestHandler {
   }
 
   @CheckForNull
-  private Characteristic findCharacteristicById(@Nullable Integer id){
+  private Characteristic findCharacteristicById(@Nullable Integer id) {
     if (id != null) {
       return technicalDebtManager.findCharacteristicById(id);
     }
index 513be22291e494997ff18b2adb7f1ac363f9ff55..85bc93c8f53d95976460b0bd039604ee093aa624 100644 (file)
@@ -58,7 +58,10 @@ import org.sonar.core.profiling.Profiling;
 import org.sonar.core.purge.PurgeProfiler;
 import org.sonar.core.resource.DefaultResourcePermissions;
 import org.sonar.core.rule.DefaultRuleFinder;
-import org.sonar.core.technicaldebt.*;
+import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager;
+import org.sonar.core.technicaldebt.TechnicalDebtModelRepository;
+import org.sonar.core.technicaldebt.TechnicalDebtModelSynchronizer;
+import org.sonar.core.technicaldebt.TechnicalDebtXMLImporter;
 import org.sonar.core.test.TestPlanPerspectiveLoader;
 import org.sonar.core.test.TestablePerspectiveLoader;
 import org.sonar.core.timemachine.Periods;
@@ -365,7 +368,6 @@ public final class Platform {
     servicesContainer.addSingleton(TechnicalDebtModelSynchronizer.class);
     servicesContainer.addSingleton(TechnicalDebtModelRepository.class);
     servicesContainer.addSingleton(TechnicalDebtXMLImporter.class);
-    servicesContainer.addSingleton(TechnicalDebtConverter.class);
     servicesContainer.addSingleton(DebtFormatter.class);
     servicesContainer.addSingleton(DefaultTechnicalDebtManager.class);
 
index 5a44a0f432e7fb1742059b14f1589ddd1363bc5a..faa2f8b71753878ea9c92372f366466ff7198856 100644 (file)
@@ -21,7 +21,7 @@
 package org.sonar.server.technicaldebt;
 
 import org.sonar.api.ServerComponent;
-import org.sonar.api.issue.internal.WorkDayDuration;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.core.i18n.DefaultI18n;
 
 import java.util.Locale;
@@ -34,7 +34,7 @@ public class DebtFormatter implements ServerComponent {
     this.defaultI18n = defaultI18n;
   }
 
-  public String format(Locale locale, WorkDayDuration technicalDebt) {
+  public String format(Locale locale, WorkUnit technicalDebt) {
     StringBuilder message = new StringBuilder();
     if (technicalDebt.days() > 0) {
       message.append(defaultI18n.message(locale, "issue.technical_debt.x_days", null, technicalDebt.days()));
index fe89ec33b3be04e4aeb19b0daad07fc3943cbef4..cb079c12dae10b465f7e22958987f05ff718419b 100644 (file)
@@ -21,8 +21,8 @@
 package org.sonar.server.technicaldebt;
 
 import org.sonar.api.ServerComponent;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.technicaldebt.server.Characteristic;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager;
 import org.sonar.server.user.UserSession;
 
@@ -40,12 +40,12 @@ public class DebtService implements ServerComponent {
     this.finder = finder;
   }
 
-  public String format(WorkDayDuration technicalDebt) {
+  public String format(WorkUnit technicalDebt) {
     return debtFormatter.format(UserSession.get().locale(), technicalDebt);
   }
 
-  public WorkDayDuration toTechnicalDebt(String technicalDebtInLong) {
-    return WorkDayDuration.fromLong(Long.parseLong(technicalDebtInLong));
+  public WorkUnit toTechnicalDebt(String technicalDebtInLong) {
+    return WorkUnit.fromLong(Long.parseLong(technicalDebtInLong));
   }
 
   public List<Characteristic> findRootCharacteristics() {
index 9b43022c7d1b73a72c6080b29a6c6b7d574d4e3f..754a06a61b52ec9a0cf4df467ec1b3057b07605b 100644 (file)
@@ -28,10 +28,10 @@ import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.IssueQuery;
 import org.sonar.api.issue.IssueQueryResult;
 import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.user.User;
 import org.sonar.api.user.UserFinder;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.core.component.ComponentDto;
 import org.sonar.core.issue.DefaultActionPlan;
 import org.sonar.core.issue.db.IssueChangeDao;
@@ -270,7 +270,7 @@ public class DefaultIssueFinderTest {
     when(userFinder.findByLogins(anyListOf(String.class))).thenReturn(Lists.<User>newArrayList(
       new DefaultUser().setLogin("perceval").setName("Perceval"),
       new DefaultUser().setLogin("arthur").setName("Roi Arthur")
-      ));
+    ));
 
     IssueQuery query = IssueQuery.builder().build();
 
@@ -322,7 +322,7 @@ public class DefaultIssueFinderTest {
 
     assertThat(results.issues()).hasSize(1);
     DefaultIssue result = (DefaultIssue) results.issues().iterator().next();
-    assertThat(result.technicalDebt()).isEqualTo(WorkDayDuration.of(10, 0, 0));
+    assertThat(result.technicalDebt()).isEqualTo(new WorkUnit.Builder().setMinutes(10).build());
   }
 
 }
index ff90a7cc425ad670ae2e8e9d226b4ea08fddbcc7..bb84b7a2b8f1a6186641484e8d88344c9dd2d13c 100644 (file)
@@ -25,7 +25,7 @@ import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.api.issue.internal.WorkDayDuration;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.core.i18n.DefaultI18n;
 import org.sonar.server.technicaldebt.DebtFormatter;
 
@@ -130,8 +130,8 @@ public class IssueChangelogFormatterTest {
     FieldDiffs diffs = new FieldDiffs();
     diffs.setDiff("technicalDebt", "500", "10000");
 
-    when(debtFormatter.format(DEFAULT_LOCALE, WorkDayDuration.of(0, 5, 0))).thenReturn("5 hours");
-    when(debtFormatter.format(DEFAULT_LOCALE, WorkDayDuration.of(0, 0, 1))).thenReturn("1 days");
+    when(debtFormatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setHours(5).build())).thenReturn("5 hours");
+    when(debtFormatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setDays(1).build())).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");
@@ -148,7 +148,7 @@ public class IssueChangelogFormatterTest {
     FieldDiffs diffs = new FieldDiffs();
     diffs.setDiff("technicalDebt", null, "10000");
 
-    when(debtFormatter.format(DEFAULT_LOCALE, WorkDayDuration.of(0, 0, 1))).thenReturn("1 days");
+    when(debtFormatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setDays(1).build())).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 aa2ec681d0b4a0563d8519690455d419a06e3507..73bdd1ba6561c5312dbfbf408eec72d88b5cda03 100644 (file)
@@ -35,7 +35,6 @@ import org.sonar.api.issue.action.Action;
 import org.sonar.api.issue.internal.DefaultIssue;
 import org.sonar.api.issue.internal.DefaultIssueComment;
 import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.server.ws.WsTester;
@@ -43,6 +42,7 @@ import org.sonar.api.technicaldebt.server.Characteristic;
 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.WorkUnit;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.DefaultActionPlan;
 import org.sonar.core.issue.DefaultIssueQueryResult;
@@ -209,9 +209,8 @@ public class IssueShowWsHandlerTest {
 
   @Test
   public void show_issue_with_technical_debt() throws Exception {
-    WorkDayDuration technicalDebt = WorkDayDuration.of(1, 2, 0);
-    Issue issue = createStandardIssue()
-      .setTechnicalDebt(technicalDebt);
+    WorkUnit technicalDebt = new WorkUnit.Builder().setHours(2).setMinutes(1).build();
+    Issue issue = createStandardIssue().setTechnicalDebt(technicalDebt);
     issues.add(issue);
 
     when(debtFormatter.format(any(Locale.class), eq(technicalDebt))).thenReturn("2 hours 1 minutes");
@@ -223,9 +222,9 @@ public class IssueShowWsHandlerTest {
 
   @Test
   public void show_issue_with_characteristics() throws Exception {
-    WorkDayDuration technicalDebt = WorkDayDuration.of(1, 2, 0);
-    Issue issue = createStandardIssue()
-      .setTechnicalDebt(technicalDebt);
+    WorkUnit technicalDebt = new WorkUnit.Builder().setHours(2).setMinutes(1).build();
+    ;
+    Issue issue = createStandardIssue().setTechnicalDebt(technicalDebt);
     issues.add(issue);
 
     Characteristic requirement = new DefaultCharacteristic().setId(5).setParentId(2).setRootId(1);
index f7249fb21b5c1789ba6e6eeb655ae295b8a0e388..c9f12afd90e6eee3f61e4be005fa1903eb30b927 100644 (file)
@@ -21,7 +21,7 @@
 package org.sonar.server.technicaldebt;
 
 import org.junit.Test;
-import org.sonar.api.issue.internal.WorkDayDuration;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.core.i18n.DefaultI18n;
 
 import java.util.Locale;
@@ -43,14 +43,13 @@ public class DebtFormatterTest {
     when(i18n.message(DEFAULT_LOCALE, "issue.technical_debt.x_hours", null, 2)).thenReturn("2 hours");
     when(i18n.message(DEFAULT_LOCALE, "issue.technical_debt.x_minutes", null, 1)).thenReturn("1 minutes");
 
-    assertThat(formatter.format(DEFAULT_LOCALE, WorkDayDuration.of(0, 0, 5))).isEqualTo("5 days");
-    assertThat(formatter.format(DEFAULT_LOCALE, WorkDayDuration.of(0, 2, 0))).isEqualTo("2 hours");
-    assertThat(formatter.format(DEFAULT_LOCALE, WorkDayDuration.of(1, 0, 0))).isEqualTo("1 minutes");
+    assertThat(formatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setDays(5).build())).isEqualTo("5 days");
+    assertThat(formatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setHours(2).build())).isEqualTo("2 hours");
+    assertThat(formatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setMinutes(1).build())).isEqualTo("1 minutes");
 
-    assertThat(formatter.format(DEFAULT_LOCALE, WorkDayDuration.of(0, 2, 5))).isEqualTo("5 days 2 hours");
-    assertThat(formatter.format(DEFAULT_LOCALE, WorkDayDuration.of(1, 2, 0))).isEqualTo("2 hours 1 minutes");
-    assertThat(formatter.format(DEFAULT_LOCALE, WorkDayDuration.of(1, 2, 5))).isEqualTo("5 days 2 hours");
+    assertThat(formatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setDays(5).setHours(2).build())).isEqualTo("5 days 2 hours");
+    assertThat(formatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setHours(2).setMinutes(1).build())).isEqualTo("2 hours 1 minutes");
+    assertThat(formatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setDays(5).setHours(2).setMinutes(10).build())).isEqualTo("5 days 2 hours");
   }
 
-
 }
index 586444b6f5e8fe1dad93b0bb9263b1c9ac07f2c1..75684adf2db02f126918968a43986dd71a4eeab8 100644 (file)
@@ -20,9 +20,9 @@
 package org.sonar.server.technicaldebt;
 
 import org.junit.Test;
-import org.sonar.api.issue.internal.WorkDayDuration;
 import org.sonar.api.technicaldebt.server.Characteristic;
 import org.sonar.api.technicaldebt.server.internal.DefaultCharacteristic;
+import org.sonar.api.utils.WorkUnit;
 import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager;
 
 import java.util.List;
@@ -42,14 +42,14 @@ public class DebtServiceTest {
 
   @Test
   public void format() {
-    WorkDayDuration technicalDebt = WorkDayDuration.of(5, 0, 0);
+    WorkUnit technicalDebt = new WorkUnit.Builder().setMinutes(5).build();
     service.format(technicalDebt);
     verify(debtFormatter).format(any(Locale.class), eq(technicalDebt));
   }
 
   @Test
   public void to_technical_debt() {
-    assertThat(service.toTechnicalDebt("500")).isEqualTo(WorkDayDuration.of(0, 5, 0));
+    assertThat(service.toTechnicalDebt("500")).isEqualTo(new WorkUnit.Builder().setHours(5).build());
   }
 
   @Test