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.sensors;
22 import com.google.common.collect.Maps;
23 import org.sonar.api.batch.Decorator;
24 import org.sonar.api.batch.DecoratorContext;
25 import org.sonar.api.batch.DependedUpon;
26 import org.sonar.api.batch.DependsUpon;
27 import org.sonar.api.measures.CoreMetrics;
28 import org.sonar.api.measures.Measure;
29 import org.sonar.api.measures.MeasureUtils;
30 import org.sonar.api.measures.Metric;
31 import org.sonar.api.resources.Project;
32 import org.sonar.api.resources.Resource;
33 import org.sonar.api.resources.ResourceUtils;
34 import org.sonar.api.rules.Violation;
35 import org.sonar.batch.components.PastSnapshot;
36 import org.sonar.batch.components.TimeMachineConfiguration;
37 import org.sonar.core.review.ReviewDao;
38 import org.sonar.core.review.ReviewDto;
39 import org.sonar.core.review.ReviewPredicates;
41 import java.util.Arrays;
42 import java.util.Collection;
43 import java.util.Date;
44 import java.util.List;
48 * Decorator that creates measures related to reviews.
52 @DependsUpon(ReviewWorkflowDecorator.END_OF_REVIEWS_UPDATES)
53 public class ReviewsMeasuresDecorator implements Decorator {
55 private ReviewDao reviewDao;
56 private TimeMachineConfiguration timeMachineConfiguration;
58 public ReviewsMeasuresDecorator(ReviewDao reviewDao, TimeMachineConfiguration timeMachineConfiguration) {
59 this.reviewDao = reviewDao;
60 this.timeMachineConfiguration = timeMachineConfiguration;
63 public boolean shouldExecuteOnProject(Project project) {
64 return project.isLatestAnalysis();
68 public Collection<Metric> generatesMetrics() {
69 return Arrays.asList(CoreMetrics.ACTIVE_REVIEWS, CoreMetrics.UNASSIGNED_REVIEWS, CoreMetrics.UNPLANNED_REVIEWS, CoreMetrics.FALSE_POSITIVE_REVIEWS,
70 CoreMetrics.UNREVIEWED_VIOLATIONS, CoreMetrics.NEW_UNREVIEWED_VIOLATIONS);
73 @SuppressWarnings({"rawtypes"})
74 public void decorate(Resource resource, DecoratorContext context) {
75 if (!ResourceUtils.isPersistable(resource) || resource.getId() == null) {
79 // Load open reviews (used for counting and also for tracking new violations without a review)
80 Collection<ReviewDto> openReviews = reviewDao.selectOpenByResourceId(resource.getId(),
81 ReviewPredicates.status(ReviewDto.STATUS_OPEN, ReviewDto.STATUS_REOPENED));
83 Map<Integer, ReviewDto> openReviewsByViolationPermanentId = Maps.newHashMap();
84 int countUnassigned = 0;
86 for (ReviewDto openReview : openReviews) {
87 openReviewsByViolationPermanentId.put(openReview.getViolationPermanentId(), openReview);
88 if (openReview.getAssigneeId() == null) {
91 if (openReview.getActionPlanId() == null) {
96 int totalOpenReviews = openReviews.size() + sumChildren(resource, context, CoreMetrics.ACTIVE_REVIEWS);
97 context.saveMeasure(CoreMetrics.ACTIVE_REVIEWS, (double) totalOpenReviews);
98 context.saveMeasure(CoreMetrics.UNASSIGNED_REVIEWS, (double) (countUnassigned + sumChildren(resource, context, CoreMetrics.UNASSIGNED_REVIEWS)));
99 context.saveMeasure(CoreMetrics.UNPLANNED_REVIEWS, (double) (unplanned + sumChildren(resource, context, CoreMetrics.UNPLANNED_REVIEWS)));
101 Collection<ReviewDto> falsePositives = reviewDao.selectOpenByResourceId(resource.getId(),
102 ReviewPredicates.resolution(ReviewDto.RESOLUTION_FALSE_POSITIVE));
104 context.saveMeasure(CoreMetrics.FALSE_POSITIVE_REVIEWS, (double) (falsePositives.size() + sumChildren(resource, context, CoreMetrics.FALSE_POSITIVE_REVIEWS)));
106 Double violationsCount = MeasureUtils.getValue(context.getMeasure(CoreMetrics.VIOLATIONS), 0.0);
107 context.saveMeasure(CoreMetrics.UNREVIEWED_VIOLATIONS, violationsCount - totalOpenReviews);
109 trackNewViolationsWithoutReview(context, openReviewsByViolationPermanentId);
112 protected void trackNewViolationsWithoutReview(DecoratorContext context, Map<Integer, ReviewDto> openReviewsByViolationPermanentIds) {
113 List<Violation> violations = context.getViolations();
114 Measure measure = new Measure(CoreMetrics.NEW_UNREVIEWED_VIOLATIONS);
115 for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
116 int newUnreviewedViolations = countNewUnreviewedViolationsForSnapshot(pastSnapshot, violations, openReviewsByViolationPermanentIds);
117 int variationIndex = pastSnapshot.getIndex();
118 Collection<Measure> children = context.getChildrenMeasures(CoreMetrics.NEW_UNREVIEWED_VIOLATIONS);
119 double sumNewUnreviewedViolations = MeasureUtils.sumOnVariation(true, variationIndex, children) + newUnreviewedViolations;
120 measure.setVariation(variationIndex, sumNewUnreviewedViolations);
122 context.saveMeasure(measure);
125 protected int countNewUnreviewedViolationsForSnapshot(PastSnapshot pastSnapshot, List<Violation> violations,
126 Map<Integer, ReviewDto> openReviewsByViolationPermanentIds) {
127 Date targetDate = pastSnapshot.getTargetDate();
128 int newViolationCount = 0;
129 int newReviewedViolationCount = 0;
130 for (Violation violation : violations) {
131 if (isAfter(violation, targetDate)) {
132 newViolationCount += 1;
133 if (openReviewsByViolationPermanentIds.get(violation.getPermanentId()) != null) {
134 newReviewedViolationCount += 1;
138 return newViolationCount - newReviewedViolationCount;
141 private int sumChildren(Resource<?> resource, DecoratorContext context, Metric metric) {
143 if (!ResourceUtils.isFile(resource)) {
144 sum = MeasureUtils.sum(true, context.getChildrenMeasures(metric)).intValue();
149 private boolean isAfter(Violation violation, Date date) {
153 return violation.getCreatedAt() != null && violation.getCreatedAt().after(date);