2 * Sonar, open source software quality management tool.
3 * Copyright (C) 2008-2011 SonarSource
4 * mailto:contact AT sonarsource DOT com
6 * Sonar 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 * Sonar 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
17 * License along with Sonar; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
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.PastSnapshot;
35 import org.sonar.batch.components.TimeMachineConfiguration;
36 import org.sonar.core.NotDryRun;
38 import java.util.Arrays;
39 import java.util.Date;
40 import java.util.List;
47 @DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE)
48 public final class NewCoverageFileAnalyzer implements Decorator {
50 private List<PeriodStruct> structs;
52 public NewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration) {
53 structs = Lists.newArrayList();
54 for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
55 structs.add(new PeriodStruct(pastSnapshot));
59 NewCoverageFileAnalyzer(List<PeriodStruct> structs) {
60 this.structs = structs;
63 public boolean shouldExecuteOnProject(Project project) {
64 return project.isLatestAnalysis() && !structs.isEmpty();
67 private boolean shouldDecorate(Resource resource) {
68 return Scopes.isFile(resource) && !Qualifiers.UNIT_TEST_FILE.equals(resource.getQualifier());
72 public List<Metric> dependsOnMetrics() {
73 return Arrays.asList(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, CoreMetrics.COVERAGE_LINE_HITS_DATA,
74 CoreMetrics.CONDITIONS_BY_LINE, CoreMetrics.COVERED_CONDITIONS_BY_LINE);
78 public List<Metric> generatesNewCoverageMetrics() {
79 return Arrays.asList(CoreMetrics.NEW_LINES_TO_COVER, CoreMetrics.NEW_UNCOVERED_LINES,
80 CoreMetrics.NEW_CONDITIONS_TO_COVER, CoreMetrics.NEW_UNCOVERED_CONDITIONS);
83 public void decorate(Resource resource, DecoratorContext context) {
84 if (shouldDecorate(resource)) {
89 void doDecorate(DecoratorContext context) {
95 private boolean parse(DecoratorContext context) {
96 Measure lastCommits = context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE);
97 Measure hitsByLineMeasure = context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA);
99 if (lastCommits != null && lastCommits.hasData() && hitsByLineMeasure != null && hitsByLineMeasure.hasData()) {
100 Map<Integer, Date> datesByLine = KeyValueFormat.parseIntDateTime(lastCommits.getData());
101 Map<Integer, Integer> hitsByLine = parseCountByLine(hitsByLineMeasure);
102 Map<Integer, Integer> conditionsByLine = parseCountByLine(context.getMeasure(CoreMetrics.CONDITIONS_BY_LINE));
103 Map<Integer, Integer> coveredConditionsByLine = parseCountByLine(context.getMeasure(CoreMetrics.COVERED_CONDITIONS_BY_LINE));
107 for (Map.Entry<Integer, Integer> entry : hitsByLine.entrySet()) {
108 int lineId = entry.getKey();
109 int hits = entry.getValue();
110 int conditions = (Integer)ObjectUtils.defaultIfNull(conditionsByLine.get(lineId), 0);
111 int coveredConditions = (Integer)ObjectUtils.defaultIfNull(coveredConditionsByLine.get(lineId), 0);
112 Date date = datesByLine.get(lineId);
113 for (PeriodStruct struct : structs) {
114 struct.analyze(date, hits, conditions, coveredConditions);
123 private void reset() {
124 for (PeriodStruct struct : structs) {
129 private void compute(DecoratorContext context) {
130 Measure newLines = new Measure(CoreMetrics.NEW_LINES_TO_COVER);
131 Measure newUncoveredLines = new Measure(CoreMetrics.NEW_UNCOVERED_LINES);
132 Measure newConditions = new Measure(CoreMetrics.NEW_CONDITIONS_TO_COVER);
133 Measure newUncoveredConditions = new Measure(CoreMetrics.NEW_UNCOVERED_CONDITIONS);
135 for (PeriodStruct struct : structs) {
136 newLines.setVariation(struct.index, (double)struct.newLines);
137 newUncoveredLines.setVariation(struct.index, (double) (struct.newLines - struct.newCoveredLines));
138 newConditions.setVariation(struct.index, (double)struct.newConditions);
139 newUncoveredConditions.setVariation(struct.index, (double)struct.newConditions-struct.newCoveredConditions);
142 context.saveMeasure(newLines);
143 context.saveMeasure(newUncoveredLines);
144 context.saveMeasure(newConditions);
145 context.saveMeasure(newUncoveredConditions);
148 private Map<Integer, Integer> parseCountByLine(Measure measure) {
149 if (measure != null && measure.hasData()) {
150 return KeyValueFormat.parseIntInt(measure.getData());
152 return Maps.newHashMap();
155 public static final class PeriodStruct {
158 int newLines = 0, newCoveredLines = 0, newConditions = 0, newCoveredConditions = 0;
160 PeriodStruct(PastSnapshot pastSnapshot) {
161 this.index = pastSnapshot.getIndex();
162 this.date = pastSnapshot.getTargetDate();
165 PeriodStruct(int index, Date date) {
174 newCoveredConditions = 0;
177 void analyze(Date lineDate, int hits, int conditions, int coveredConditions) {
178 if (lineDate == null) {
181 } else if (date == null || lineDate.after(date)) {
182 // TODO test if string comparison is faster or not
184 addConditions(conditions, coveredConditions);
188 void addLine(boolean covered) {
191 newCoveredLines += 1;
195 void addConditions(int count, int countCovered) {
196 newConditions += count;
198 newCoveredConditions += countCovered;