2 * SonarQube, open source software quality management tool.
3 * Copyright (C) 2008-2013 SonarSource
4 * mailto:contact AT sonarsource DOT com
6 * SonarQube is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * SonarQube is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.plugins.core.timemachine;
22 import com.google.common.collect.Lists;
23 import com.google.common.collect.Maps;
24 import org.apache.commons.lang.ObjectUtils;
25 import org.sonar.api.batch.*;
26 import org.sonar.api.measures.CoreMetrics;
27 import org.sonar.api.measures.Measure;
28 import org.sonar.api.measures.Metric;
29 import org.sonar.api.resources.Project;
30 import org.sonar.api.resources.Qualifiers;
31 import org.sonar.api.resources.Resource;
32 import org.sonar.api.resources.Scopes;
33 import org.sonar.api.utils.KeyValueFormat;
34 import org.sonar.batch.components.Period;
35 import org.sonar.batch.components.TimeMachineConfiguration;
36 import org.sonar.core.DryRunIncompatible;
38 import java.util.Arrays;
39 import java.util.Date;
40 import java.util.List;
47 @DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE)
48 public abstract class AbstractNewCoverageFileAnalyzer implements Decorator {
50 private List<PeriodStruct> structs;
52 public AbstractNewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration) {
53 structs = Lists.newArrayList();
54 for (Period period : timeMachineConfiguration.periods()) {
55 structs.add(new PeriodStruct(period.getIndex(), period.getTargetDate()));
59 AbstractNewCoverageFileAnalyzer(List<PeriodStruct> structs) {
60 this.structs = structs;
63 public abstract Metric getCoverageLineHitsDataMetric();
65 public abstract Metric getConditionsByLineMetric();
67 public abstract Metric getCoveredConditionsByLineMetric();
69 public abstract Metric getNewLinesToCoverMetric();
71 public abstract Metric getNewUncoveredLinesMetric();
73 public abstract Metric getNewConditionsToCoverMetric();
75 public abstract Metric getNewUncoveredConditionsMetric();
77 public boolean shouldExecuteOnProject(Project project) {
78 return !structs.isEmpty();
81 private boolean shouldDecorate(Resource resource) {
82 return Scopes.isFile(resource) && !Qualifiers.UNIT_TEST_FILE.equals(resource.getQualifier());
86 public List<Metric> dependsOnMetrics() {
88 return Arrays.asList(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE,
89 getCoverageLineHitsDataMetric(), getConditionsByLineMetric(), getCoveredConditionsByLineMetric());
93 public List<Metric> generatesNewCoverageMetrics() {
94 return Arrays.asList(getNewLinesToCoverMetric(), getNewUncoveredLinesMetric(), getNewConditionsToCoverMetric(), getNewUncoveredConditionsMetric());
97 public void decorate(Resource resource, DecoratorContext context) {
98 if (shouldDecorate(resource)) {
103 void doDecorate(DecoratorContext context) {
104 if (parse(context)) {
109 private boolean parse(DecoratorContext context) {
110 Measure lastCommits = context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE);
111 Measure hitsByLineMeasure = context.getMeasure(getCoverageLineHitsDataMetric());
113 if (lastCommits != null && lastCommits.hasData() && hitsByLineMeasure != null && hitsByLineMeasure.hasData()) {
114 Map<Integer, Date> datesByLine = KeyValueFormat.parseIntDateTime(lastCommits.getData());
115 Map<Integer, Integer> hitsByLine = parseCountByLine(hitsByLineMeasure);
116 Map<Integer, Integer> conditionsByLine = parseCountByLine(context.getMeasure(getConditionsByLineMetric()));
117 Map<Integer, Integer> coveredConditionsByLine = parseCountByLine(context.getMeasure(getCoveredConditionsByLineMetric()));
121 for (Map.Entry<Integer, Integer> entry : hitsByLine.entrySet()) {
122 int lineId = entry.getKey();
123 int hits = entry.getValue();
124 int conditions = (Integer) ObjectUtils.defaultIfNull(conditionsByLine.get(lineId), 0);
125 int coveredConditions = (Integer) ObjectUtils.defaultIfNull(coveredConditionsByLine.get(lineId), 0);
126 Date date = datesByLine.get(lineId);
127 for (PeriodStruct struct : structs) {
128 struct.analyze(date, hits, conditions, coveredConditions);
137 private void reset() {
138 for (PeriodStruct struct : structs) {
143 private void compute(DecoratorContext context) {
144 Measure newLines = new Measure(getNewLinesToCoverMetric());
145 Measure newUncoveredLines = new Measure(getNewUncoveredLinesMetric());
146 Measure newConditions = new Measure(getNewConditionsToCoverMetric());
147 Measure newUncoveredConditions = new Measure(getNewUncoveredConditionsMetric());
149 for (PeriodStruct struct : structs) {
150 if (struct.hasNewCode()) {
151 newLines.setVariation(struct.index, (double) struct.getNewLines());
152 newUncoveredLines.setVariation(struct.index, (double) (struct.getNewLines() - struct.getNewCoveredLines()));
153 newConditions.setVariation(struct.index, (double) struct.getNewConditions());
154 newUncoveredConditions.setVariation(struct.index, (double) struct.getNewConditions() - struct.getNewCoveredConditions());
158 context.saveMeasure(newLines);
159 context.saveMeasure(newUncoveredLines);
160 context.saveMeasure(newConditions);
161 context.saveMeasure(newUncoveredConditions);
164 private Map<Integer, Integer> parseCountByLine(Measure measure) {
165 if (measure != null && measure.hasData()) {
166 return KeyValueFormat.parseIntInt(measure.getData());
168 return Maps.newHashMap();
171 public static final class PeriodStruct {
175 Integer newCoveredLines;
176 Integer newConditions;
177 Integer newCoveredConditions;
179 PeriodStruct(int index, Date date) {
186 newCoveredLines = null;
187 newConditions = null;
188 newCoveredConditions = null;
191 void analyze(Date lineDate, int hits, int conditions, int coveredConditions) {
192 if (lineDate == null) {
195 } else if (date == null || lineDate.after(date)) {
196 // TODO test if string comparison is faster or not
198 addConditions(conditions, coveredConditions);
202 void addLine(boolean covered) {
203 if (newLines == null) {
208 if (newCoveredLines == null) {
211 newCoveredLines += 1;
215 void addConditions(int count, int countCovered) {
216 if (newConditions == null) {
219 newConditions += count;
221 if (newCoveredConditions == null) {
222 newCoveredConditions = 0;
224 newCoveredConditions += countCovered;
228 boolean hasNewCode() {
229 return newLines != null;
232 public int getNewLines() {
233 return newLines != null ? newLines : 0;
236 public int getNewCoveredLines() {
237 return newCoveredLines != null ? newCoveredLines : 0;
240 public int getNewConditions() {
241 return newConditions != null ? newConditions : 0;
244 public int getNewCoveredConditions() {
245 return newCoveredConditions != null ? newCoveredConditions : 0;