From fcb07c6af06f952f5c241470ffaef47b6d5df8ed Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Thu, 3 Apr 2014 17:52:05 +0200 Subject: [PATCH] Create a new DebtModel API on batch side --- .../core/technicaldebt/DebtDecorator.java | 2 +- .../core/technicaldebt/DebtDecoratorTest.java | 4 +- .../sonar/batch/debt/DebtModelProvider.java | 64 +++++----- .../org/sonar/batch/issue/ModuleIssues.java | 2 +- .../org/sonar/batch/rule/RulesProvider.java | 17 +-- .../batch/scan/ProjectScanContainer.java | 30 +---- .../batch/debt/DebtModelProviderTest.java | 36 +++--- .../sonar/batch/issue/ModuleIssuesTest.java | 12 +- .../sonar/batch/rule/RulesProviderTest.java | 51 ++++---- .../DefaultTechnicalDebtModel.java | 47 ++++--- .../DefaultTechnicalDebtModelTest.java | 64 ++++++---- .../api/batch/debt/DebtCharacteristic.java | 37 ++++++ .../org/sonar/api/batch/debt/DebtModel.java | 58 +++++++++ .../internal/DefaultDebtCharacteristic.java | 120 ++++++++++++++++++ .../batch/debt/internal/DefaultDebtModel.java | 93 ++++++++++++++ .../java/org/sonar/api/batch/rule/Rule.java | 4 +- .../api/batch/rule/internal/DefaultRule.java | 8 +- .../api/batch/rule/internal/NewRule.java | 6 +- .../api/server/debt/DebtCharacteristic.java | 11 +- .../org/sonar/api/server/debt/DebtModel.java | 5 +- .../internal/DefaultDebtCharacteristic.java | 9 +- .../technicaldebt/batch/Characteristic.java | 2 + .../batch/TechnicalDebtModel.java | 5 +- .../debt/internal/DefaultDebtModelTest.java | 88 +++++++++++++ .../batch/rule/internal/RulesBuilderTest.java | 8 +- 25 files changed, 593 insertions(+), 190 deletions(-) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtCharacteristic.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtModel.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/internal/DefaultDebtCharacteristic.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/internal/DefaultDebtModel.java create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/batch/debt/internal/DefaultDebtModelTest.java diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/DebtDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/DebtDecorator.java index d948742da26..2caf164d9c2 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/DebtDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/DebtDecorator.java @@ -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) { diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/DebtDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/DebtDecoratorTest.java index aff369faab5..2aac5804446 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/DebtDecoratorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/DebtDecoratorTest.java @@ -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())); diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java b/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java index be7d9895cc2..a00b9f909f5 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java @@ -20,28 +20,30 @@ 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 dtos = dao.selectEnabledCharacteristics(); - Map characteristicsById = newHashMap(); + private DebtModel load(CharacteristicDao dao) { + DefaultDebtModel debtModel = new DefaultDebtModel(); - addRootCharacteristics(debtModel, dtos, characteristicsById); - addCharacteristics(dtos, characteristicsById); + List 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 dtos, Map 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 allCharacteristics) { + return Iterables.find(allCharacteristics, new Predicate() { + @Override + public boolean apply(@Nullable CharacteristicDto input) { + return input != null && id == input.getId(); } - } + }); } - private void addCharacteristics(List dtos, Map 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()); } + } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java index 1e6bb240720..5eaa0726038 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java @@ -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())); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java b/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java index 1cbf9b1aa5b..ef479b27f00 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java @@ -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 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)); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index b610b00338b..dee73ecd6c7 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -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 diff --git a/sonar-batch/src/test/java/org/sonar/batch/debt/DebtModelProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/debt/DebtModelProviderTest.java index 66bf2768902..a61e4e18063 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/debt/DebtModelProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/debt/DebtModelProviderTest.java @@ -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(); } - } diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java index 5a5f8ef282d..6be76b5d95d 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java @@ -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(); diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProviderTest.java index ffcab662476..5ec693d2bc1 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProviderTest.java @@ -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(); } diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModel.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModel.java index c5d58b984d4..8e82b618bdd 100644 --- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModel.java +++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModel.java @@ -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 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 rootCharacteristics() { - return newArrayList(Iterables.filter(rootCharacteristics, new Predicate() { + return newArrayList(Iterables.filter(characteristics(), new Predicate() { @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() { @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 characteristics() { List 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()); } } diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModelTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModelTest.java index 6bde3927995..a22036ed58e 100644 --- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModelTest.java +++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModelTest.java @@ -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 index 00000000000..01a3a97fe29 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtCharacteristic.java @@ -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 index 00000000000..b5aa6691f4b --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtModel.java @@ -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 characteristics(); + + /** + * Return sub-characteristics of a characteristic + */ + List subCharacteristics(String characteristicKey); + + /** + * Return characteristics and sub-characteristics + */ + List 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 index 00000000000..bea0f94cce6 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/internal/DefaultDebtCharacteristic.java @@ -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 index 00000000000..853412ac236 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/internal/DefaultDebtModel.java @@ -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 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 characteristics() { + return newArrayList(characteristicsByKey.get(null)); + } + + @Override + public List subCharacteristics(String characteristicKey) { + return newArrayList(characteristicsByKey.get(characteristicKey)); + } + + @Override + public List allCharacteristics() { + return newArrayList(characteristicsByKey.values()); + } + + @Override + @CheckForNull + public DebtCharacteristic characteristicByKey(final String key) { + return Iterables.find(characteristicsByKey.values(), new Predicate() { + @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() { + @Override + public boolean apply(DebtCharacteristic input) { + return id == ((DefaultDebtCharacteristic) input).id(); + } + }, null); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rule.java index f87317dfdab..475d8e7b600 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rule.java @@ -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) diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRule.java index 5b2db0d27b9..cd93c077d60 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRule.java @@ -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 builder = ImmutableMap.builder(); @@ -97,8 +97,8 @@ public class DefaultRule implements Rule { } @Override - public String debtCharacteristic() { - return debtCharacteristic; + public String debtSubCharacteristic() { + return debtSubCharacteristic; } @Override diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewRule.java index c3506ff1b50..c921a41ecc4 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewRule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewRule.java @@ -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 params = new HashMap(); @@ -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; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtCharacteristic.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtCharacteristic.java index 81d704ced4a..c8e428213a0 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtCharacteristic.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtCharacteristic.java @@ -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(); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtModel.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtModel.java index cfc8f323cba..b2d668fa239 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtModel.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtModel.java @@ -39,6 +39,9 @@ public interface DebtModel extends ServerComponent { */ List characteristics(); - DebtCharacteristic characteristicById(int id); + /** + * Return a characteristic or a sub-characteristic by its key + */ + DebtCharacteristic characteristicByKey(String key); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/internal/DefaultDebtCharacteristic.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/internal/DefaultDebtCharacteristic.java index d9df13d4d76..9852e6c7cc1 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/internal/DefaultDebtCharacteristic.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/internal/DefaultDebtCharacteristic.java @@ -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); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Characteristic.java b/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Characteristic.java index 27287a14498..fadad455575 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Characteristic.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Characteristic.java @@ -27,7 +27,9 @@ import java.util.List; /** * @since 4.1 + * @deprecated since 4.3 */ +@Deprecated public interface Characteristic { Integer id(); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/TechnicalDebtModel.java b/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/TechnicalDebtModel.java index a18212ec83e..33eb7e48a76 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/TechnicalDebtModel.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/TechnicalDebtModel.java @@ -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 index 00000000000..561cb5ed3ae --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/debt/internal/DefaultDebtModelTest.java @@ -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(); + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/RulesBuilderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/RulesBuilderTest.java index e569d4e1db1..074fe58aef7 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/RulesBuilderTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/RulesBuilderTest.java @@ -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(); } -- 2.39.5