001 /*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * SonarQube is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
019 */
020
021 package org.sonar.batch.rule;
022
023 import com.google.common.collect.ArrayListMultimap;
024 import com.google.common.collect.ListMultimap;
025 import org.picocontainer.injectors.ProviderAdapter;
026 import org.slf4j.Logger;
027 import org.slf4j.LoggerFactory;
028 import org.sonar.api.batch.debt.DebtCharacteristic;
029 import org.sonar.api.batch.debt.DebtModel;
030 import org.sonar.api.batch.debt.DebtRemediationFunction;
031 import org.sonar.api.batch.debt.internal.DefaultDebtModel;
032 import org.sonar.api.batch.rule.Rules;
033 import org.sonar.api.batch.rule.internal.NewRule;
034 import org.sonar.api.batch.rule.internal.RulesBuilder;
035 import org.sonar.api.rule.RuleKey;
036 import org.sonar.api.utils.Durations;
037 import org.sonar.api.utils.TimeProfiler;
038 import org.sonar.core.rule.RuleDao;
039 import org.sonar.core.rule.RuleDto;
040 import org.sonar.core.rule.RuleParamDto;
041
042 import javax.annotation.Nullable;
043
044 import java.util.List;
045
046 /**
047 * Loads all enabled and non manual rules
048 */
049 public class RulesProvider extends ProviderAdapter {
050
051 private static final Logger LOG = LoggerFactory.getLogger(RulesProvider.class);
052
053 private Rules singleton = null;
054
055 public Rules provide(RuleDao ruleDao, DebtModel debtModel, Durations durations) {
056 if (singleton == null) {
057 TimeProfiler profiler = new TimeProfiler(LOG).start("Loading rules");
058 singleton = load(ruleDao, (DefaultDebtModel) debtModel, durations);
059 profiler.stop();
060 }
061 return singleton;
062 }
063
064 private Rules load(RuleDao ruleDao, DefaultDebtModel debtModel, Durations durations) {
065 RulesBuilder rulesBuilder = new RulesBuilder();
066
067 List<RuleParamDto> ruleParamDtos = ruleDao.selectParameters();
068 ListMultimap<Integer, RuleParamDto> paramDtosByRuleId = ArrayListMultimap.create();
069 for (RuleParamDto dto : ruleParamDtos) {
070 paramDtosByRuleId.put(dto.getRuleId(), dto);
071 }
072 for (RuleDto ruleDto : ruleDao.selectEnablesAndNonManual()) {
073 RuleKey ruleKey = RuleKey.of(ruleDto.getRepositoryKey(), ruleDto.getRuleKey());
074 NewRule newRule = rulesBuilder.add(ruleKey)
075 .setId(ruleDto.getId())
076 .setName(ruleDto.getName())
077 .setSeverity(ruleDto.getSeverityString())
078 .setDescription(ruleDto.getDescription())
079 .setStatus(ruleDto.getStatus())
080 .setInternalKey(ruleDto.getConfigKey());
081
082 if (hasCharacteristic(ruleDto)) {
083 newRule.setDebtSubCharacteristic(effectiveCharacteristic(ruleDto, ruleKey, debtModel).key());
084 newRule.setDebtRemediationFunction(effectiveFunction(ruleDto, ruleKey, durations));
085 }
086
087 for (RuleParamDto ruleParamDto : paramDtosByRuleId.get(ruleDto.getId())) {
088 newRule.addParam(ruleParamDto.getName())
089 .setDescription(ruleParamDto.getDescription());
090 }
091 }
092 return rulesBuilder.build();
093 }
094
095 private DebtCharacteristic effectiveCharacteristic(RuleDto ruleDto, RuleKey ruleKey, DefaultDebtModel debtModel) {
096 Integer subCharacteristicId = ruleDto.getSubCharacteristicId();
097 Integer defaultSubCharacteristicId = ruleDto.getDefaultSubCharacteristicId();
098 Integer effectiveSubCharacteristicId = subCharacteristicId != null ? subCharacteristicId : defaultSubCharacteristicId;
099 DebtCharacteristic subCharacteristic = debtModel.characteristicById(effectiveSubCharacteristicId);
100 if (subCharacteristic == null) {
101 throw new IllegalStateException(String.format("Sub characteristic id '%s' on rule '%s' has not been found", effectiveSubCharacteristicId, ruleKey));
102 }
103 return subCharacteristic;
104 }
105
106 private DebtRemediationFunction effectiveFunction(RuleDto ruleDto, RuleKey ruleKey, Durations durations) {
107 String function = ruleDto.getRemediationFunction();
108 String defaultFunction = ruleDto.getDefaultRemediationFunction();
109 if (function != null) {
110 return createDebtRemediationFunction(function, ruleDto.getRemediationCoefficient(), ruleDto.getRemediationOffset(), durations);
111 } else if (defaultFunction != null) {
112 return createDebtRemediationFunction(defaultFunction, ruleDto.getDefaultRemediationCoefficient(), ruleDto.getDefaultRemediationOffset(), durations);
113 } else {
114 throw new IllegalStateException(String.format("Remediation function should not be null on rule '%s'", ruleKey));
115 }
116 }
117
118 private DebtRemediationFunction createDebtRemediationFunction(String function, @Nullable String factor, @Nullable String offset, Durations durations) {
119 return DebtRemediationFunction.create(DebtRemediationFunction.Type.valueOf(function),
120 factor != null ? durations.decode(factor) : null,
121 offset != null ? durations.decode(offset) : null);
122 }
123
124 /**
125 * Return true is the characteristic has not been overridden and a default characteristic is existing or
126 * if the characteristic has been overridden but is not disabled
127 */
128 private boolean hasCharacteristic(RuleDto ruleDto){
129 Integer subCharacteristicId = ruleDto.getSubCharacteristicId();
130 return (subCharacteristicId == null && ruleDto.getDefaultSubCharacteristicId() != null) ||
131 (subCharacteristicId != null && !RuleDto.DISABLED_CHARACTERISTIC_ID.equals(subCharacteristicId));
132 }
133
134 }