]> source.dussan.org Git - sonarqube.git/commitdiff
Create a new DebtModel API on batch side
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 3 Apr 2014 15:52:05 +0000 (17:52 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 3 Apr 2014 15:52:32 +0000 (17:52 +0200)
25 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/DebtDecorator.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/DebtDecoratorTest.java
sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java
sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/test/java/org/sonar/batch/debt/DebtModelProviderTest.java
sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
sonar-batch/src/test/java/org/sonar/batch/rule/RulesProviderTest.java
sonar-core/src/main/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModel.java
sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModelTest.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtCharacteristic.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtModel.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/internal/DefaultDebtCharacteristic.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/internal/DefaultDebtModel.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rule.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRule.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewRule.java
sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtCharacteristic.java
sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtModel.java
sonar-plugin-api/src/main/java/org/sonar/api/server/debt/internal/DefaultDebtCharacteristic.java
sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Characteristic.java
sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/TechnicalDebtModel.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/debt/internal/DefaultDebtModelTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/RulesBuilderTest.java

index d948742da26925e0707597e89e5464ba9e8a7f68..2caf164d9c2ffb189f6fe4a03f5f22723c8be4ed 100644 (file)
@@ -121,7 +121,7 @@ public final class DebtDecorator implements Decorator {
     if (debt != null) {
       Rule rule = rules.find(ruleKey);
       if (rule != null) {
-        String characteristicKey = rule.debtCharacteristic();
+        String characteristicKey = rule.debtSubCharacteristic();
         if (characteristicKey != null) {
           Characteristic characteristic = model.characteristicByKey(characteristicKey);
           if (characteristic != null) {
index aff369faab5d16a114026c80b3179c6a623d1da9..2aac5804446f125122740fd16161af92fde13898 100644 (file)
@@ -98,8 +98,8 @@ public class DebtDecoratorTest {
   public void before() throws Exception {
     when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
     RulesBuilder rulesBuilder = new RulesBuilder();
-    rulesBuilder.add(ruleKey1).setName("rule1").setDebtCharacteristic("MEMORY_EFFICIENCY");
-    rulesBuilder.add(ruleKey2).setName("rule2").setDebtCharacteristic("MODULARITY");
+    rulesBuilder.add(ruleKey1).setName("rule1").setDebtSubCharacteristic("MEMORY_EFFICIENCY");
+    rulesBuilder.add(ruleKey2).setName("rule2").setDebtSubCharacteristic("MODULARITY");
     rules = rulesBuilder.build();
 
     when(ruleFinder.findByKey(ruleKey1)).thenReturn(org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()));
index be7d9895cc29a6ff9c6fa4a51678b3f8d70c0c1d..a00b9f909f51b425e5d091656804af365bbd24b4 100644 (file)
 
 package org.sonar.batch.debt;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
 import org.picocontainer.injectors.ProviderAdapter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
-import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
+import org.sonar.api.batch.debt.DebtCharacteristic;
+import org.sonar.api.batch.debt.DebtModel;
+import org.sonar.api.batch.debt.internal.DefaultDebtCharacteristic;
+import org.sonar.api.batch.debt.internal.DefaultDebtModel;
 import org.sonar.api.utils.TimeProfiler;
-import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel;
 import org.sonar.core.technicaldebt.db.CharacteristicDao;
 import org.sonar.core.technicaldebt.db.CharacteristicDto;
 
-import java.util.List;
-import java.util.Map;
+import javax.annotation.Nullable;
 
-import static com.google.common.collect.Maps.newHashMap;
+import java.util.List;
 
 public class DebtModelProvider extends ProviderAdapter {
 
   private static final Logger LOG = LoggerFactory.getLogger(DebtModelProvider.class);
 
-  private TechnicalDebtModel model;
+  private DebtModel model;
 
-  public TechnicalDebtModel provide(CharacteristicDao dao) {
+  public DebtModel provide(CharacteristicDao dao) {
     if (model == null) {
       TimeProfiler profiler = new TimeProfiler(LOG).start("Loading technical debt model");
       model = load(dao);
@@ -50,33 +52,37 @@ public class DebtModelProvider extends ProviderAdapter {
     return model;
   }
 
-  private TechnicalDebtModel load(CharacteristicDao dao) {
-    DefaultTechnicalDebtModel debtModel = new DefaultTechnicalDebtModel();
-    List<CharacteristicDto> dtos = dao.selectEnabledCharacteristics();
-    Map<Integer, DefaultCharacteristic> characteristicsById = newHashMap();
+  private DebtModel load(CharacteristicDao dao) {
+    DefaultDebtModel debtModel = new DefaultDebtModel();
 
-    addRootCharacteristics(debtModel, dtos, characteristicsById);
-    addCharacteristics(dtos, characteristicsById);
+    List<CharacteristicDto> allCharacteristics = dao.selectEnabledCharacteristics();
+    for (CharacteristicDto dto : allCharacteristics) {
+      Integer parentId = dto.getParentId();
+      if (parentId == null) {
+        debtModel.addCharacteristic(toDebtCharacteristic(dto));
+      } else {
+        debtModel.addSubCharacteristic(toDebtCharacteristic(dto), characteristicById(parentId, allCharacteristics).getKey());
+      }
+    }
     return debtModel;
   }
 
-  private void addRootCharacteristics(DefaultTechnicalDebtModel model, List<CharacteristicDto> dtos, Map<Integer, DefaultCharacteristic> characteristicsById) {
-    for (CharacteristicDto dto : dtos) {
-      if (dto.getParentId() == null) {
-        DefaultCharacteristic rootCharacteristic = dto.toCharacteristic(null);
-        model.addRootCharacteristic(rootCharacteristic);
-        characteristicsById.put(dto.getId(), rootCharacteristic);
+  private static CharacteristicDto characteristicById(final int id, List<CharacteristicDto> allCharacteristics) {
+    return Iterables.find(allCharacteristics, new Predicate<CharacteristicDto>() {
+      @Override
+      public boolean apply(@Nullable CharacteristicDto input) {
+        return input != null && id == input.getId();
       }
-    }
+    });
   }
 
-  private void addCharacteristics(List<CharacteristicDto> dtos, Map<Integer, DefaultCharacteristic> characteristicsById) {
-    for (CharacteristicDto dto : dtos) {
-      if (dto.getParentId() != null) {
-        DefaultCharacteristic parent = characteristicsById.get(dto.getParentId());
-        DefaultCharacteristic characteristic = dto.toCharacteristic(parent);
-        characteristicsById.put(dto.getId(), characteristic);
-      }
-    }
+  private static DebtCharacteristic toDebtCharacteristic(CharacteristicDto characteristic) {
+    return new DefaultDebtCharacteristic()
+      .setId(characteristic.getId())
+      .setKey(characteristic.getKey())
+      .setName(characteristic.getName())
+      .setOrder(characteristic.getOrder())
+      .setParentId(characteristic.getParentId());
   }
+
 }
index 1e6bb2407206ea2b6f6e58c82014731e9390e0fe..5eaa0726038f59afcbf415d4d4c46f147dff48a8 100644 (file)
@@ -108,7 +108,7 @@ public class ModuleIssues {
       issue.setSeverity(activeRule.severity());
     }
     DebtRemediationFunction function = rule.debtRemediationFunction();
-    if (rule.debtCharacteristic() != null && function != null) {
+    if (rule.debtSubCharacteristic() != null && function != null) {
       issue.setDebt(calculateDebt(function, issue.effortToFix(), rule.key()));
     }
   }
index 1cbf9b1aa5b53c5f20a734028d9310fb0dcdff93..ef479b27f00efaf603f9c0304346ff7a5b4ac352 100644 (file)
@@ -25,14 +25,15 @@ import com.google.common.collect.ListMultimap;
 import org.picocontainer.injectors.ProviderAdapter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.debt.DebtCharacteristic;
+import org.sonar.api.batch.debt.DebtModel;
+import org.sonar.api.batch.debt.internal.DefaultDebtModel;
 import org.sonar.api.batch.rule.DebtRemediationFunction;
 import org.sonar.api.batch.rule.Rules;
 import org.sonar.api.batch.rule.internal.NewRule;
 import org.sonar.api.batch.rule.internal.RulesBuilder;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
-import org.sonar.api.technicaldebt.batch.Characteristic;
-import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
 import org.sonar.api.utils.Durations;
 import org.sonar.api.utils.TimeProfiler;
 import org.sonar.core.rule.RuleDao;
@@ -52,16 +53,16 @@ public class RulesProvider extends ProviderAdapter {
 
   private Rules singleton = null;
 
-  public Rules provide(RuleDao ruleDao, TechnicalDebtModel debtModel, Durations durations) {
+  public Rules provide(RuleDao ruleDao, DebtModel debtModel, Durations durations) {
     if (singleton == null) {
       TimeProfiler profiler = new TimeProfiler(LOG).start("Loading rules");
-      singleton = load(ruleDao, debtModel, durations);
+      singleton = load(ruleDao, (DefaultDebtModel) debtModel, durations);
       profiler.stop();
     }
     return singleton;
   }
 
-  private Rules load(RuleDao ruleDao, TechnicalDebtModel debtModel, Durations durations) {
+  private Rules load(RuleDao ruleDao, DefaultDebtModel debtModel, Durations durations) {
     RulesBuilder rulesBuilder = new RulesBuilder();
 
     List<RuleParamDto> ruleParamDtos = ruleDao.selectParameters();
@@ -80,7 +81,7 @@ public class RulesProvider extends ProviderAdapter {
       // TODO should we set metadata ?
 
       if (hasCharacteristic(ruleDto)) {
-        newRule.setDebtCharacteristic(effectiveCharacteristic(ruleDto, ruleKey, debtModel).key());
+        newRule.setDebtSubCharacteristic(effectiveCharacteristic(ruleDto, ruleKey, debtModel).key());
         newRule.setDebtRemediationFunction(effectiveFunction(ruleDto, ruleKey, durations));
       }
 
@@ -92,11 +93,11 @@ public class RulesProvider extends ProviderAdapter {
     return rulesBuilder.build();
   }
 
-  private Characteristic effectiveCharacteristic(RuleDto ruleDto, RuleKey ruleKey, TechnicalDebtModel debtModel) {
+  private DebtCharacteristic effectiveCharacteristic(RuleDto ruleDto, RuleKey ruleKey, DefaultDebtModel debtModel) {
     Integer subCharacteristicId = ruleDto.getSubCharacteristicId();
     Integer defaultSubCharacteristicId = ruleDto.getDefaultSubCharacteristicId();
     Integer effectiveSubCharacteristicId = subCharacteristicId != null ? subCharacteristicId : defaultSubCharacteristicId;
-    Characteristic subCharacteristic = debtModel.characteristicById(effectiveSubCharacteristicId);
+    DebtCharacteristic subCharacteristic = debtModel.characteristicById(effectiveSubCharacteristicId);
     if (subCharacteristic == null) {
       throw new IllegalStateException(String.format("Sub characteristic id '%s' on rule '%s' has not been found", effectiveSubCharacteristicId, ruleKey));
     }
index b610b00338b05d81518b1607a3cf19110bae72b0..dee73ecd6c71d8a729adb6741807e3913779809c 100644 (file)
@@ -35,34 +35,12 @@ 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.debt.DebtModelProvider;
 import org.sonar.batch.debt.IssueChangelogDebtCalculator;
-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.rule.RulesProvider;
@@ -77,6 +55,7 @@ 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.DefaultTechnicalDebtModel;
 import org.sonar.core.test.TestPlanBuilder;
 import org.sonar.core.test.TestPlanPerspectiveLoader;
 import org.sonar.core.test.TestableBuilder;
@@ -180,6 +159,7 @@ public class ProjectScanContainer extends ComponentContainer {
       SymbolizableBuilder.class,
 
       // technical debt
+      DefaultTechnicalDebtModel.class,
       new DebtModelProvider(),
 
       // rules
index 66bf2768902737eb40da8dae9063defafc94048e..a61e4e180636f1ddbb26a0add9a5669bc63d287a 100644 (file)
@@ -25,8 +25,8 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
-import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
-import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel;
+import org.sonar.api.batch.debt.DebtCharacteristic;
+import org.sonar.api.batch.debt.DebtModel;
 import org.sonar.core.technicaldebt.db.CharacteristicDao;
 import org.sonar.core.technicaldebt.db.CharacteristicDto;
 
@@ -42,18 +42,18 @@ public class DebtModelProviderTest {
 
   DebtModelProvider provider;
 
-
   @Before
   public void before() {
     provider = new DebtModelProvider();
   }
 
   @Test
-  public void find_all() throws Exception {
+  public void provide_model() throws Exception {
     CharacteristicDto rootCharacteristicDto = new CharacteristicDto()
       .setId(1)
       .setKey("MEMORY_EFFICIENCY")
-      .setName("Memory use");
+      .setName("Memory use")
+      .setOrder(1);
 
     CharacteristicDto characteristicDto = new CharacteristicDto()
       .setId(2)
@@ -63,21 +63,19 @@ public class DebtModelProviderTest {
 
     when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(rootCharacteristicDto, characteristicDto));
 
-    DefaultTechnicalDebtModel result = (DefaultTechnicalDebtModel) provider.provide(dao);
-    assertThat(result.rootCharacteristics()).hasSize(1);
+    DebtModel result = provider.provide(dao);
+    assertThat(result.characteristics()).hasSize(1);
 
-    DefaultCharacteristic rootCharacteristic = result.characteristicByKey("MEMORY_EFFICIENCY");
-    assertThat(rootCharacteristic.key()).isEqualTo("MEMORY_EFFICIENCY");
-    assertThat(rootCharacteristic.name()).isEqualTo("Memory use");
-    assertThat(rootCharacteristic.parent()).isNull();
-    assertThat(rootCharacteristic.children()).hasSize(1);
-    assertThat(rootCharacteristic.children().get(0).key()).isEqualTo("EFFICIENCY");
+    DebtCharacteristic characteristic = result.characteristicByKey("MEMORY_EFFICIENCY");
+    assertThat(characteristic.key()).isEqualTo("MEMORY_EFFICIENCY");
+    assertThat(characteristic.name()).isEqualTo("Memory use");
+    assertThat(characteristic.isSub()).isFalse();
+    assertThat(characteristic.order()).isEqualTo(1);
 
-    DefaultCharacteristic characteristic = result.characteristicByKey("EFFICIENCY");
-    assertThat(characteristic.key()).isEqualTo("EFFICIENCY");
-    assertThat(characteristic.name()).isEqualTo("Efficiency");
-    assertThat(characteristic.parent().key()).isEqualTo("MEMORY_EFFICIENCY");
-    assertThat(characteristic.children()).isEmpty();
+    DebtCharacteristic subCharacteristic = result.characteristicByKey("EFFICIENCY");
+    assertThat(subCharacteristic.key()).isEqualTo("EFFICIENCY");
+    assertThat(subCharacteristic.name()).isEqualTo("Efficiency");
+    assertThat(subCharacteristic.isSub()).isTrue();
+    assertThat(subCharacteristic.order()).isNull();
   }
-
 }
index 5a5f8ef282dd8942bdbd38e0956860292e8a8eec..6be76b5d95dbf40bf30389417551cd32f8902796 100644 (file)
@@ -47,9 +47,7 @@ import static org.fest.assertions.Assertions.assertThat;
 import static org.fest.assertions.Fail.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
 
 @RunWith(MockitoJUnitRunner.class)
 public class ModuleIssuesTest {
@@ -252,7 +250,7 @@ public class ModuleIssuesTest {
   public void set_debt_with_linear_function() throws Exception {
     ruleBuilder.add(SQUID_RULE_KEY)
       .setName(SQUID_RULE_NAME)
-      .setDebtCharacteristic("COMPILER_RELATED_PORTABILITY")
+      .setDebtSubCharacteristic("COMPILER_RELATED_PORTABILITY")
       .setDebtRemediationFunction(DebtRemediationFunction.createLinear(Duration.create(10L)));
     activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO);
     initModuleIssues();
@@ -278,7 +276,7 @@ public class ModuleIssuesTest {
   public void set_debt_with_linear_with_offset_function() throws Exception {
     ruleBuilder.add(SQUID_RULE_KEY)
       .setName(SQUID_RULE_NAME)
-      .setDebtCharacteristic("COMPILER_RELATED_PORTABILITY")
+      .setDebtSubCharacteristic("COMPILER_RELATED_PORTABILITY")
       .setDebtRemediationFunction(DebtRemediationFunction.createLinearWithOffset(Duration.create(10L), Duration.create(25L)));
     activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO);
     initModuleIssues();
@@ -304,7 +302,7 @@ public class ModuleIssuesTest {
   public void set_debt_with_constant_issue_function() throws Exception {
     ruleBuilder.add(SQUID_RULE_KEY)
       .setName(SQUID_RULE_NAME)
-      .setDebtCharacteristic("COMPILER_RELATED_PORTABILITY")
+      .setDebtSubCharacteristic("COMPILER_RELATED_PORTABILITY")
       .setDebtRemediationFunction(DebtRemediationFunction.createConstantPerIssue(Duration.create(10L)));
     activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO);
     initModuleIssues();
@@ -330,7 +328,7 @@ public class ModuleIssuesTest {
   public void fail_to_set_debt_with_constant_issue_function_when_effort_to_fix_is_set() throws Exception {
     ruleBuilder.add(SQUID_RULE_KEY)
       .setName(SQUID_RULE_NAME)
-      .setDebtCharacteristic("COMPILER_RELATED_PORTABILITY")
+      .setDebtSubCharacteristic("COMPILER_RELATED_PORTABILITY")
       .setDebtRemediationFunction(DebtRemediationFunction.createConstantPerIssue(Duration.create(25L)));
     activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO);
     initModuleIssues();
index ffcab662476bc23bae76b49ba9ea40f4719d57c0..5ec693d2bc1205587c6dfb6d6a6378a217af93ad 100644 (file)
@@ -25,6 +25,8 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.batch.debt.internal.DefaultDebtCharacteristic;
+import org.sonar.api.batch.debt.internal.DefaultDebtModel;
 import org.sonar.api.batch.rule.DebtRemediationFunction;
 import org.sonar.api.batch.rule.Rule;
 import org.sonar.api.batch.rule.RuleParam;
@@ -32,12 +34,10 @@ import org.sonar.api.batch.rule.Rules;
 import org.sonar.api.config.Settings;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
-import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
 import org.sonar.api.utils.Duration;
 import org.sonar.api.utils.Durations;
 import org.sonar.core.persistence.AbstractDaoTestCase;
 import org.sonar.core.rule.RuleDao;
-import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel;
 
 import static org.fest.assertions.Assertions.assertThat;
 import static org.fest.assertions.Fail.fail;
@@ -50,29 +50,34 @@ public class RulesProviderTest extends AbstractDaoTestCase {
 
   RuleDao ruleDao;
 
-  DefaultTechnicalDebtModel debtModel;
+  DefaultDebtModel debtModel;
 
   RulesProvider provider;
 
   @Before
   public void setUp() throws Exception {
-    debtModel = new DefaultTechnicalDebtModel();
-    debtModel.addRootCharacteristic(new DefaultCharacteristic()
-      .setId(101)
-      .setKey("EFFICIENCY")
-      .setName("Efficiency")
-      .setParent(new DefaultCharacteristic()
+    debtModel = new DefaultDebtModel()
+      .addCharacteristic(new DefaultDebtCharacteristic()
         .setId(100)
         .setKey("MEMORY_EFFICIENCY")
-        .setName("Memory use")));
-    debtModel.addRootCharacteristic(new DefaultCharacteristic()
-      .setId(103)
-      .setKey("PORTABILITY")
-      .setName("Portability")
-      .setParent(new DefaultCharacteristic()
+        .setName("Memory use")
+        .setOrder(1))
+      .addCharacteristic(new DefaultDebtCharacteristic()
+        .setId(101)
+        .setKey("EFFICIENCY")
+        .setName("Efficiency")
+        .setParentId(100));
+    debtModel
+      .addCharacteristic(new DefaultDebtCharacteristic()
         .setId(102)
         .setKey("COMPILER_RELATED_PORTABILITY")
-        .setName("Compiler")));
+        .setName("Compiler")
+        .setOrder(1))
+      .addCharacteristic(new DefaultDebtCharacteristic()
+        .setId(103)
+        .setKey("PORTABILITY")
+        .setName("Portability")
+        .setParentId(102));
 
     durations = new Durations(new Settings().setProperty("sonar.technicalDebt.hoursInDay", 8), null);
     ruleDao = new RuleDao(getMyBatis());
@@ -111,7 +116,7 @@ public class RulesProviderTest extends AbstractDaoTestCase {
     Rules rules = provider.provide(ruleDao, debtModel, durations);
 
     Rule rule = rules.find(RuleKey.of("checkstyle", "AvoidNull"));
-    assertThat(rule.debtCharacteristic()).isEqualTo("EFFICIENCY");
+    assertThat(rule.debtSubCharacteristic()).isEqualTo("EFFICIENCY");
     assertThat(rule.debtRemediationFunction()).isEqualTo(DebtRemediationFunction.createLinearWithOffset(Duration.decode("5d", 8), Duration.decode("10h", 8)));
   }
 
@@ -122,7 +127,7 @@ public class RulesProviderTest extends AbstractDaoTestCase {
     Rules rules = provider.provide(ruleDao, debtModel, durations);
 
     Rule rule = rules.find(RuleKey.of("checkstyle", "AvoidNull"));
-    assertThat(rule.debtCharacteristic()).isEqualTo("PORTABILITY");
+    assertThat(rule.debtSubCharacteristic()).isEqualTo("PORTABILITY");
     assertThat(rule.debtRemediationFunction()).isEqualTo(DebtRemediationFunction.createLinear(Duration.decode("2h", 8)));
   }
 
@@ -134,7 +139,7 @@ public class RulesProviderTest extends AbstractDaoTestCase {
 
     // As both default columns and user columns on debt are set, user debt columns should be used
     Rule rule = rules.find(RuleKey.of("checkstyle", "AvoidNull"));
-    assertThat(rule.debtCharacteristic()).isEqualTo("PORTABILITY");
+    assertThat(rule.debtSubCharacteristic()).isEqualTo("PORTABILITY");
     assertThat(rule.debtRemediationFunction()).isEqualTo(DebtRemediationFunction.createLinear(Duration.decode("2h", 8)));
   }
 
@@ -146,7 +151,7 @@ public class RulesProviderTest extends AbstractDaoTestCase {
 
     // As both default columns and user columns on debt are set, user debt columns should be used
     Rule rule = rules.find(RuleKey.of("checkstyle", "AvoidNull"));
-    assertThat(rule.debtCharacteristic()).isEqualTo("PORTABILITY");
+    assertThat(rule.debtSubCharacteristic()).isEqualTo("PORTABILITY");
     assertThat(rule.debtRemediationFunction()).isEqualTo(DebtRemediationFunction.createLinear(Duration.decode("2h", 8)));
   }
 
@@ -158,7 +163,7 @@ public class RulesProviderTest extends AbstractDaoTestCase {
 
     // As both default columns and user columns on debt are set, user debt columns should be used
     Rule rule = rules.find(RuleKey.of("checkstyle", "AvoidNull"));
-    assertThat(rule.debtCharacteristic()).isEqualTo("PORTABILITY");
+    assertThat(rule.debtSubCharacteristic()).isEqualTo("PORTABILITY");
     assertThat(rule.debtRemediationFunction()).isEqualTo(DebtRemediationFunction.createLinear(Duration.decode("2h", 8)));
   }
 
@@ -169,7 +174,7 @@ public class RulesProviderTest extends AbstractDaoTestCase {
     Rules rules = provider.provide(ruleDao, debtModel, durations);
 
     Rule rule = rules.find(RuleKey.of("checkstyle", "AvoidNull"));
-    assertThat(rule.debtCharacteristic()).isNull();
+    assertThat(rule.debtSubCharacteristic()).isNull();
     assertThat(rule.debtRemediationFunction()).isNull();
   }
 
@@ -180,7 +185,7 @@ public class RulesProviderTest extends AbstractDaoTestCase {
     Rules rules = provider.provide(ruleDao, debtModel, durations);
 
     Rule rule = rules.find(RuleKey.of("checkstyle", "AvoidNull"));
-    assertThat(rule.debtCharacteristic()).isNull();
+    assertThat(rule.debtSubCharacteristic()).isNull();
     assertThat(rule.debtRemediationFunction()).isNull();
   }
 
index c5d58b984d4a5f45eaef8f3e113bb92601ce80dd..8e82b618bdd5f73623e361b7f54f8ed101031236 100644 (file)
@@ -22,14 +22,17 @@ package org.sonar.core.technicaldebt;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
+import org.sonar.api.batch.debt.DebtCharacteristic;
+import org.sonar.api.batch.debt.DebtModel;
+import org.sonar.api.batch.debt.internal.DefaultDebtCharacteristic;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
 import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
 import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement;
 
 import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -37,19 +40,14 @@ import static com.google.common.collect.Lists.newArrayList;
 
 public class DefaultTechnicalDebtModel implements TechnicalDebtModel {
 
-  private Collection<DefaultCharacteristic> rootCharacteristics;
+  private final DebtModel model;
 
-  public DefaultTechnicalDebtModel() {
-    rootCharacteristics = newArrayList();
-  }
-
-  public DefaultTechnicalDebtModel addRootCharacteristic(DefaultCharacteristic characteristic) {
-    rootCharacteristics.add(characteristic);
-    return this;
+  public DefaultTechnicalDebtModel(DebtModel model) {
+    this.model = model;
   }
 
   public List<DefaultCharacteristic> rootCharacteristics() {
-    return newArrayList(Iterables.filter(rootCharacteristics, new Predicate<DefaultCharacteristic>() {
+    return newArrayList(Iterables.filter(characteristics(), new Predicate<DefaultCharacteristic>() {
       @Override
       public boolean apply(DefaultCharacteristic input) {
         return input.isRoot();
@@ -68,7 +66,7 @@ public class DefaultTechnicalDebtModel implements TechnicalDebtModel {
   }
 
   @CheckForNull
-  public DefaultCharacteristic characteristicById(final Integer id){
+  public DefaultCharacteristic characteristicById(final Integer id) {
     return Iterables.find(characteristics(), new Predicate<DefaultCharacteristic>() {
       @Override
       public boolean apply(DefaultCharacteristic input) {
@@ -83,16 +81,17 @@ public class DefaultTechnicalDebtModel implements TechnicalDebtModel {
   }
 
   @CheckForNull
-  public DefaultRequirement requirementsById(final Integer id){
+  public DefaultRequirement requirementsById(final Integer id) {
     return null;
   }
 
   public List<DefaultCharacteristic> characteristics() {
     List<DefaultCharacteristic> flatCharacteristics = newArrayList();
-    for (DefaultCharacteristic rootCharacteristic : rootCharacteristics) {
-      flatCharacteristics.add(rootCharacteristic);
-      for (DefaultCharacteristic characteristic : rootCharacteristic.children()) {
-        flatCharacteristics.add(characteristic);
+    for (DebtCharacteristic characteristic : model.characteristics()) {
+      DefaultCharacteristic root = toDefaultCharacteristic((DefaultDebtCharacteristic) characteristic, null);
+      flatCharacteristics.add(root);
+      for (DebtCharacteristic subCharacteristic : model.subCharacteristics(characteristic.key())) {
+        flatCharacteristics.add(toDefaultCharacteristic((DefaultDebtCharacteristic) subCharacteristic, root));
       }
     }
     return flatCharacteristics;
@@ -102,8 +101,20 @@ public class DefaultTechnicalDebtModel implements TechnicalDebtModel {
     return Collections.emptyList();
   }
 
-  public boolean isEmpty(){
-    return rootCharacteristics.isEmpty();
+  public boolean isEmpty() {
+    return model.allCharacteristics().isEmpty();
+  }
+
+  private static DefaultCharacteristic toDefaultCharacteristic(DefaultDebtCharacteristic debtCharacteristic, @Nullable DefaultCharacteristic parentCharacteristic) {
+    return new DefaultCharacteristic()
+      .setId(debtCharacteristic.id())
+      .setKey(debtCharacteristic.key())
+      .setName(debtCharacteristic.name())
+      .setOrder(debtCharacteristic.order())
+      .setParent(parentCharacteristic)
+      .setRoot(parentCharacteristic)
+      .setCreatedAt(debtCharacteristic.createdAt())
+      .setUpdatedAt(debtCharacteristic.updatedAt());
   }
 
 }
index 6bde3927995ac09cc480d63ebb8bc8283c7a0e58..a22036ed58e0cd29e5d91d62d90bc90e20b7b4e5 100644 (file)
@@ -22,6 +22,8 @@ package org.sonar.core.technicaldebt;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.sonar.api.batch.debt.internal.DefaultDebtCharacteristic;
+import org.sonar.api.batch.debt.internal.DefaultDebtModel;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
 
@@ -33,47 +35,53 @@ public class DefaultTechnicalDebtModelTest {
 
   @Before
   public void setUp() throws Exception {
-    sqaleModel = new DefaultTechnicalDebtModel();
+    DefaultDebtModel debtModel = new DefaultDebtModel();
+    debtModel.addCharacteristic(
+      new DefaultDebtCharacteristic().setId(1)
+        .setKey("MEMORY_EFFICIENCY")
+        .setName("Memory use")
+        .setOrder(1)
+    );
+    debtModel.addSubCharacteristic(
+      new DefaultDebtCharacteristic().setId(2)
+        .setKey("EFFICIENCY")
+        .setName("Efficiency")
+        .setParentId(1),
+      "MEMORY_EFFICIENCY"
+    );
+    sqaleModel = new DefaultTechnicalDebtModel(debtModel);
   }
 
   @Test
-  public void get_root_characteristics() throws Exception {
-    DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic()
-      .setKey("MEMORY_EFFICIENCY")
-      .setName("Memory use");
-
-    new DefaultCharacteristic()
-      .setKey("EFFICIENCY")
-      .setName("Efficiency")
-      .setParent(rootCharacteristic);
-
-    sqaleModel.addRootCharacteristic(rootCharacteristic);
-
+  public void get_characteristics() throws Exception {
     assertThat(sqaleModel.rootCharacteristics()).hasSize(1);
+
     DefaultCharacteristic resultRootCharacteristic = sqaleModel.rootCharacteristics().get(0);
-    assertThat(resultRootCharacteristic).isEqualTo(rootCharacteristic);
+    assertThat(resultRootCharacteristic.id()).isEqualTo(1);
+    assertThat(resultRootCharacteristic.key()).isEqualTo("MEMORY_EFFICIENCY");
+    assertThat(resultRootCharacteristic.name()).isEqualTo("Memory use");
+    assertThat(resultRootCharacteristic.order()).isEqualTo(1);
+    assertThat(resultRootCharacteristic.children()).hasSize(1);
+    assertThat(resultRootCharacteristic.parent()).isNull();
+    assertThat(resultRootCharacteristic.root()).isNull();
   }
 
   @Test
   public void get_characteristic_by_key() throws Exception {
-    DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic()
-      .setKey("MEMORY_EFFICIENCY")
-      .setName("Memory use");
-
-    DefaultCharacteristic characteristic = new DefaultCharacteristic()
-      .setKey("EFFICIENCY")
-      .setName("Efficiency")
-      .setParent(rootCharacteristic);
-
-    sqaleModel.addRootCharacteristic(rootCharacteristic);
-
-    assertThat(sqaleModel.characteristicByKey("MEMORY_EFFICIENCY")).isEqualTo(rootCharacteristic);
-    assertThat(sqaleModel.characteristicByKey("EFFICIENCY")).isEqualTo(characteristic);
-    assertThat(sqaleModel.characteristicByKey("EFFICIENCY").parent()).isEqualTo(rootCharacteristic);
+    assertThat(sqaleModel.characteristicByKey("MEMORY_EFFICIENCY")).isNotNull();
+    assertThat(sqaleModel.characteristicByKey("EFFICIENCY")).isNotNull();
+    assertThat(sqaleModel.characteristicByKey("EFFICIENCY").parent()).isNotNull();
 
     assertThat(sqaleModel.characteristicByKey("UNKNOWN")).isNull();
   }
 
+  @Test
+  public void characteristic_by_id() throws Exception {
+    assertThat(sqaleModel.characteristicById(1)).isNotNull();
+    assertThat(sqaleModel.characteristicById(2)).isNotNull();
+    assertThat(sqaleModel.characteristicById(123)).isNull();
+  }
+
   @Test
   public void get_requirement_by_rule_key_always_return_null() throws Exception {
     assertThat(sqaleModel.requirementsByRule(RuleKey.of("checkstyle", "Regexp"))).isNull();
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtCharacteristic.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtCharacteristic.java
new file mode 100644 (file)
index 0000000..01a3a97
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.debt;
+
+import javax.annotation.CheckForNull;
+
+/**
+ * @since 4.3
+ */
+public interface DebtCharacteristic {
+  String key();
+
+  String name();
+
+  @CheckForNull
+  Integer order();
+
+  boolean isSub();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtModel.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtModel.java
new file mode 100644 (file)
index 0000000..b5aa669
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.debt;
+
+import javax.annotation.CheckForNull;
+
+import java.util.List;
+
+/**
+ * This class can be used to retrieve characteristics or sub-characteristics from the technical debt model during analysis.
+ *
+ * Unfortunately, this class cannot be used to set characteristic on {@link org.sonar.api.measures.Measure},
+ * because the Measure API still uses deprecated {@link org.sonar.api.technicaldebt.batch.Characteristic}.
+ *
+ * @since 4.3
+ */
+public interface DebtModel {
+
+  /**
+   * Return only characteristics
+   */
+  List<DebtCharacteristic> characteristics();
+
+  /**
+   * Return sub-characteristics of a characteristic
+   */
+  List<DebtCharacteristic> subCharacteristics(String characteristicKey);
+
+  /**
+   * Return characteristics and sub-characteristics
+   */
+  List<DebtCharacteristic> allCharacteristics();
+
+  /**
+   * Return a characteristic or a sub-characteristic by a key
+   */
+  @CheckForNull
+  DebtCharacteristic characteristicByKey(String key);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/internal/DefaultDebtCharacteristic.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/internal/DefaultDebtCharacteristic.java
new file mode 100644 (file)
index 0000000..bea0f94
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.debt.internal;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.sonar.api.batch.debt.DebtCharacteristic;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.util.Date;
+
+public class DefaultDebtCharacteristic implements DebtCharacteristic {
+
+  private Integer id;
+  private String key;
+  private String name;
+  private Integer order;
+  private Integer parentId;
+  private Date createdAt;
+  private Date updatedAt;
+
+  public Integer id() {
+    return id;
+  }
+
+  public DefaultDebtCharacteristic setId(Integer id) {
+    this.id = id;
+    return this;
+  }
+
+  @Override
+  public String key() {
+    return key;
+  }
+
+  public DefaultDebtCharacteristic setKey(String key) {
+    this.key = key;
+    return this;
+  }
+
+  @Override
+  public String name() {
+    return name;
+  }
+
+  public DefaultDebtCharacteristic setName(String name) {
+    this.name = name;
+    return this;
+  }
+
+  @Override
+  @CheckForNull
+  public Integer order() {
+    return order;
+  }
+
+  public DefaultDebtCharacteristic setOrder(@Nullable Integer order) {
+    this.order = order;
+    return this;
+  }
+
+  @CheckForNull
+  public Integer parentId() {
+    return parentId;
+  }
+
+  public DefaultDebtCharacteristic setParentId(@Nullable Integer parentId) {
+    this.parentId = parentId;
+    return this;
+  }
+
+  public Date createdAt() {
+    return createdAt;
+  }
+
+  public DefaultDebtCharacteristic setCreatedAt(Date createdAt) {
+    this.createdAt = createdAt;
+    return this;
+  }
+
+  @CheckForNull
+  public Date updatedAt() {
+    return updatedAt;
+  }
+
+  public DefaultDebtCharacteristic setUpdatedAt(@Nullable Date updatedAt) {
+    this.updatedAt = updatedAt;
+    return this;
+  }
+
+  @Override
+  public boolean isSub(){
+    return parentId != null;
+  }
+
+  @Override
+  public String toString() {
+    return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/internal/DefaultDebtModel.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/internal/DefaultDebtModel.java
new file mode 100644 (file)
index 0000000..853412a
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.debt.internal;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import org.sonar.api.batch.debt.DebtCharacteristic;
+import org.sonar.api.batch.debt.DebtModel;
+
+import javax.annotation.CheckForNull;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+public class DefaultDebtModel implements DebtModel {
+
+  /**
+   * Sub-characteristics list can be retrieved with the characteristic key
+   * Characteristics list can be retrieved by with the null key
+   */
+  private Multimap<String, DebtCharacteristic> characteristicsByKey;
+
+  public DefaultDebtModel() {
+    characteristicsByKey = ArrayListMultimap.create();
+  }
+
+  public DefaultDebtModel addCharacteristic(DebtCharacteristic characteristic) {
+    characteristicsByKey.put(null, characteristic);
+    return this;
+  }
+
+  public DefaultDebtModel addSubCharacteristic(DebtCharacteristic subCharacteristic, String characteristicKey) {
+    characteristicsByKey.put(characteristicKey, subCharacteristic);
+    return this;
+  }
+
+  @Override
+  public List<DebtCharacteristic> characteristics() {
+    return newArrayList(characteristicsByKey.get(null));
+  }
+
+  @Override
+  public List<DebtCharacteristic> subCharacteristics(String characteristicKey) {
+    return newArrayList(characteristicsByKey.get(characteristicKey));
+  }
+
+  @Override
+  public List<DebtCharacteristic> allCharacteristics() {
+    return newArrayList(characteristicsByKey.values());
+  }
+
+  @Override
+  @CheckForNull
+  public DebtCharacteristic characteristicByKey(final String key) {
+    return Iterables.find(characteristicsByKey.values(), new Predicate<DebtCharacteristic>() {
+      @Override
+      public boolean apply(DebtCharacteristic input) {
+        return key.equals(input.key());
+      }
+    }, null);
+  }
+
+  @CheckForNull
+  public DebtCharacteristic characteristicById(final int id) {
+    return Iterables.find(characteristicsByKey.values(), new Predicate<DebtCharacteristic>() {
+      @Override
+      public boolean apply(DebtCharacteristic input) {
+        return id == ((DefaultDebtCharacteristic) input).id();
+      }
+    }, null);
+  }
+}
index f87317dfdabd702ad0f34da45150bb3840249d6f..475d8e7b6008eb606bc9a4e55f456b9db1841a01 100644 (file)
@@ -53,12 +53,12 @@ public interface Rule {
   RuleStatus status();
 
   /**
-   * Characteristic key.
+   * Sub characteristic key.
    *
    * @since 4.3
    */
   @CheckForNull
-  String debtCharacteristic();
+  String debtSubCharacteristic();
 
   /**
    * Remediation function : can by Linear (with a coefficient), Linear with offset (with a coefficient and an offset) or Constant per issue (with an offset)
index 5b2db0d27b9f73d4dfb507ea90c4dba2b0784d92..cd93c077d60556f0715f78b0b458a296f1af2f89 100644 (file)
@@ -37,7 +37,7 @@ public class DefaultRule implements Rule {
 
   private final RuleKey key;
   private final Integer id;
-  private final String name, severity, description, metadata, debtCharacteristic;
+  private final String name, severity, description, metadata, debtSubCharacteristic;
   private final RuleStatus status;
   private final DebtRemediationFunction debtRemediationFunction;
 
@@ -51,7 +51,7 @@ public class DefaultRule implements Rule {
     this.description = newRule.description;
     this.metadata = newRule.metadata;
     this.status = newRule.status;
-    this.debtCharacteristic = newRule.debtCharacteristic;
+    this.debtSubCharacteristic = newRule.debtSubCharacteristic;
     this.debtRemediationFunction = newRule.debtRemediationFunction;
 
     ImmutableMap.Builder<String, RuleParam> builder = ImmutableMap.builder();
@@ -97,8 +97,8 @@ public class DefaultRule implements Rule {
   }
 
   @Override
-  public String debtCharacteristic() {
-    return debtCharacteristic;
+  public String debtSubCharacteristic() {
+    return debtSubCharacteristic;
   }
 
   @Override
index c3506ff1b50c5132198ce283791a557b00b4e582..c921a41ecc4c060d626fdb2548aa5e859f0ff6b2 100644 (file)
@@ -37,7 +37,7 @@ public class NewRule {
 
   final RuleKey key;
   Integer id;
-  String name, description, severity = DEFAULT_SEVERITY, metadata, debtCharacteristic;
+  String name, description, severity = DEFAULT_SEVERITY, metadata, debtSubCharacteristic;
   DebtRemediationFunction debtRemediationFunction;
   RuleStatus status = RuleStatus.defaultStatus();
   Map<String, NewRuleParam> params = new HashMap<String, NewRuleParam>();
@@ -76,8 +76,8 @@ public class NewRule {
     return this;
   }
 
-  public NewRule setDebtCharacteristic(@Nullable String c) {
-    this.debtCharacteristic = c;
+  public NewRule setDebtSubCharacteristic(@Nullable String c) {
+    this.debtSubCharacteristic = c;
     return this;
   }
 
index 81d704ced4af17ddebdceb3bf9fdfa5bfb39c090..c8e428213a06a7d088b9dabbd17ec7eaf4a39200 100644 (file)
@@ -22,13 +22,10 @@ package org.sonar.api.server.debt;
 
 import javax.annotation.CheckForNull;
 
-import java.util.Date;
-
 /**
  * @since 4.3
  */
 public interface DebtCharacteristic {
-  Integer id();
 
   String key();
 
@@ -37,11 +34,5 @@ public interface DebtCharacteristic {
   @CheckForNull
   Integer order();
 
-  @CheckForNull
-  Integer parentId();
-
-  Date createdAt();
-
-  @CheckForNull
-  Date updatedAt();
+  boolean isSub();
 }
index cfc8f323cbab03405ce9c9782acdb4c0ca2b3028..b2d668fa239362a9dadd9b9753db76324b5dc47f 100644 (file)
@@ -39,6 +39,9 @@ public interface DebtModel extends ServerComponent {
    */
   List<DebtCharacteristic> characteristics();
 
-  DebtCharacteristic characteristicById(int id);
+  /**
+   * Return a characteristic or a sub-characteristic by its key
+   */
+  DebtCharacteristic characteristicByKey(String key);
 
 }
index d9df13d4d76692b9ac36d6b5d7c19a83cb06de7a..9852e6c7cc164ae957f592a580cb70975e1cce3b 100644 (file)
@@ -42,7 +42,6 @@ public class DefaultDebtCharacteristic implements DebtCharacteristic {
   private Date createdAt;
   private Date updatedAt;
 
-  @Override
   public Integer id() {
     return id;
   }
@@ -83,7 +82,6 @@ public class DefaultDebtCharacteristic implements DebtCharacteristic {
     return this;
   }
 
-  @Override
   @CheckForNull
   public Integer parentId() {
     return parentId;
@@ -94,7 +92,6 @@ public class DefaultDebtCharacteristic implements DebtCharacteristic {
     return this;
   }
 
-  @Override
   public Date createdAt() {
     return createdAt;
   }
@@ -104,7 +101,6 @@ public class DefaultDebtCharacteristic implements DebtCharacteristic {
     return this;
   }
 
-  @Override
   @CheckForNull
   public Date updatedAt() {
     return updatedAt;
@@ -115,6 +111,11 @@ public class DefaultDebtCharacteristic implements DebtCharacteristic {
     return this;
   }
 
+  @Override
+  public boolean isSub(){
+    return parentId != null;
+  }
+
   @Override
   public String toString() {
     return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
index 27287a14498b359ea20f79e2b22bb75e27185e1c..fadad455575287ea637f92684e01394927319260 100644 (file)
@@ -27,7 +27,9 @@ import java.util.List;
 
 /**
  * @since 4.1
+ * @deprecated since 4.3
  */
+@Deprecated
 public interface Characteristic {
 
   Integer id();
index a18212ec83ec480e587f82c9e8407413eea6917f..33eb7e48a7613ab315f5c02f31cf0c34098f5f32 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.sonar.api.technicaldebt.batch;
 
+import org.sonar.api.BatchComponent;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
 
@@ -30,8 +31,10 @@ import java.util.List;
 /**
  * @since 4.1
  * Used by Views plugin
+ * @deprecated since 4.3
  */
-public interface TechnicalDebtModel {
+@Deprecated
+public interface TechnicalDebtModel extends BatchComponent {
 
   @CheckForNull
   Characteristic characteristicById(Integer id);
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/debt/internal/DefaultDebtModelTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/debt/internal/DefaultDebtModelTest.java
new file mode 100644 (file)
index 0000000..561cb5e
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.debt.internal;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class DefaultDebtModelTest {
+
+  private DefaultDebtModel debtModel;
+
+  @Before
+  public void setUp() throws Exception {
+    debtModel = new DefaultDebtModel()
+      .addCharacteristic(
+        new DefaultDebtCharacteristic().setId(1)
+          .setKey("MEMORY_EFFICIENCY")
+          .setName("Memory use")
+          .setOrder(1)
+      )
+      .addSubCharacteristic(
+        new DefaultDebtCharacteristic().setId(2)
+          .setKey("EFFICIENCY")
+          .setName("Efficiency")
+          .setParentId(1),
+        "MEMORY_EFFICIENCY"
+      );
+  }
+
+  @Test
+  public void all_characteristics() throws Exception {
+    assertThat(debtModel.allCharacteristics()).hasSize(2);
+  }
+
+  @Test
+  public void characteristics() throws Exception {
+    assertThat(debtModel.characteristics()).hasSize(1);
+  }
+
+  @Test
+  public void sub_characteristics() throws Exception {
+    assertThat(debtModel.subCharacteristics("MEMORY_EFFICIENCY")).hasSize(1);
+  }
+
+  @Test
+  public void characteristic_by_id() throws Exception {
+    DefaultDebtCharacteristic debtCharacteristic = (DefaultDebtCharacteristic) debtModel.characteristicById(1);
+    assertThat(debtCharacteristic).isNotNull();
+    assertThat(debtCharacteristic.id()).isEqualTo(1);
+    assertThat(debtCharacteristic.key()).isEqualTo("MEMORY_EFFICIENCY");
+    assertThat(debtCharacteristic.name()).isEqualTo("Memory use");
+    assertThat(debtCharacteristic.order()).isEqualTo(1);
+    assertThat(debtCharacteristic.parentId()).isNull();
+    assertThat(debtCharacteristic.isSub()).isFalse();
+  }
+
+  @Test
+  public void characteristic_by_key() throws Exception {
+    DefaultDebtCharacteristic debtCharacteristic = (DefaultDebtCharacteristic) debtModel.characteristicByKey("EFFICIENCY");
+    assertThat(debtCharacteristic).isNotNull();
+    assertThat(debtCharacteristic.id()).isEqualTo(2);
+    assertThat(debtCharacteristic.key()).isEqualTo("EFFICIENCY");
+    assertThat(debtCharacteristic.name()).isEqualTo("Efficiency");
+    assertThat(debtCharacteristic.order()).isNull();
+    assertThat(debtCharacteristic.parentId()).isEqualTo(1);
+    assertThat(debtCharacteristic.isSub()).isTrue();
+  }
+}
index e569d4e1db1892ff0d6e891b3a25bde36f96db0c..074fe58aef7dd113ac1c8fb4eaad09cc3008533a 100644 (file)
@@ -48,7 +48,7 @@ public class RulesBuilderTest {
     newSquid1.setMetadata("foo=bar");
     newSquid1.setSeverity(Severity.CRITICAL);
     newSquid1.setStatus(RuleStatus.BETA);
-    newSquid1.setDebtCharacteristic("COMPILER");
+    newSquid1.setDebtSubCharacteristic("COMPILER");
     newSquid1.setDebtRemediationFunction(DebtRemediationFunction.create(DebtRemediationFunction.Type.LINEAR_OFFSET, Duration.create(10), Duration.create(60)));
     newSquid1.addParam("min");
     newSquid1.addParam("max").setDescription("Maximum");
@@ -71,11 +71,11 @@ public class RulesBuilderTest {
     assertThat(squid1.metadata()).isEqualTo("foo=bar");
     assertThat(squid1.status()).isEqualTo(RuleStatus.BETA);
     assertThat(squid1.severity()).isEqualTo(Severity.CRITICAL);
-    assertThat(squid1.debtCharacteristic()).isEqualTo("COMPILER");
+    assertThat(squid1.debtSubCharacteristic()).isEqualTo("COMPILER");
     assertThat(squid1.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET);
     assertThat(squid1.debtRemediationFunction().coefficient()).isEqualTo(Duration.create(10));
     assertThat(squid1.debtRemediationFunction().offset()).isEqualTo(Duration.create(60));
-    assertThat(squid1.debtCharacteristic()).isEqualTo("COMPILER");
+    assertThat(squid1.debtSubCharacteristic()).isEqualTo("COMPILER");
     assertThat(squid1.params()).hasSize(2);
     assertThat(squid1.param("min").key()).isEqualTo("min");
     assertThat(squid1.param("min").description()).isNull();
@@ -89,7 +89,7 @@ public class RulesBuilderTest {
     assertThat(squid2.metadata()).isNull();
     assertThat(squid2.status()).isEqualTo(RuleStatus.defaultStatus());
     assertThat(squid2.severity()).isEqualTo(Severity.defaultSeverity());
-    assertThat(squid2.debtCharacteristic()).isNull();
+    assertThat(squid2.debtSubCharacteristic()).isNull();
     assertThat(squid2.debtRemediationFunction()).isNull();
     assertThat(squid2.params()).isEmpty();
   }