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    }