]> source.dussan.org Git - sonarqube.git/blob
f526a433e8fbd5a12bcd97c8771d6424517d4f81
[sonarqube.git] /
1 /*
2  * SonarQube, open source software quality management tool.
3  * Copyright (C) 2008-2013 SonarSource
4  * mailto:contact AT sonarsource DOT com
5  *
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.
10  *
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.
15  *
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.
19  */
20 package org.sonar.plugins.core.sensors;
21
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;
40
41 import java.util.Arrays;
42 import java.util.Collection;
43 import java.util.Date;
44 import java.util.List;
45 import java.util.Map;
46
47 /**
48  * Decorator that creates measures related to reviews.
49  *
50  * @since 2.14
51  */
52 @DependsUpon(ReviewWorkflowDecorator.END_OF_REVIEWS_UPDATES)
53 public class ReviewsMeasuresDecorator implements Decorator {
54
55   private ReviewDao reviewDao;
56   private TimeMachineConfiguration timeMachineConfiguration;
57
58   public ReviewsMeasuresDecorator(ReviewDao reviewDao, TimeMachineConfiguration timeMachineConfiguration) {
59     this.reviewDao = reviewDao;
60     this.timeMachineConfiguration = timeMachineConfiguration;
61   }
62
63   public boolean shouldExecuteOnProject(Project project) {
64     return project.isLatestAnalysis();
65   }
66
67   @DependedUpon
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);
71   }
72
73   @SuppressWarnings({"rawtypes"})
74   public void decorate(Resource resource, DecoratorContext context) {
75     if (!ResourceUtils.isPersistable(resource) || resource.getId() == null) {
76       return;
77     }
78
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));
82
83     Map<Integer, ReviewDto> openReviewsByViolationPermanentId = Maps.newHashMap();
84     int countUnassigned = 0;
85     int unplanned = 0;
86     for (ReviewDto openReview : openReviews) {
87       openReviewsByViolationPermanentId.put(openReview.getViolationPermanentId(), openReview);
88       if (openReview.getAssigneeId() == null) {
89         countUnassigned++;
90       }
91       if (openReview.getActionPlanId() == null) {
92         unplanned++;
93       }
94     }
95
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)));
100
101     Collection<ReviewDto> falsePositives = reviewDao.selectOpenByResourceId(resource.getId(),
102         ReviewPredicates.resolution(ReviewDto.RESOLUTION_FALSE_POSITIVE));
103
104     context.saveMeasure(CoreMetrics.FALSE_POSITIVE_REVIEWS, (double) (falsePositives.size() + sumChildren(resource, context, CoreMetrics.FALSE_POSITIVE_REVIEWS)));
105
106     Double violationsCount = MeasureUtils.getValue(context.getMeasure(CoreMetrics.VIOLATIONS), 0.0);
107     context.saveMeasure(CoreMetrics.UNREVIEWED_VIOLATIONS, violationsCount - totalOpenReviews);
108
109     trackNewViolationsWithoutReview(context, openReviewsByViolationPermanentId);
110   }
111
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);
121     }
122     context.saveMeasure(measure);
123   }
124
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;
135         }
136       }
137     }
138     return newViolationCount - newReviewedViolationCount;
139   }
140
141   private int sumChildren(Resource<?> resource, DecoratorContext context, Metric metric) {
142     int sum = 0;
143     if (!ResourceUtils.isFile(resource)) {
144       sum = MeasureUtils.sum(true, context.getChildrenMeasures(metric)).intValue();
145     }
146     return sum;
147   }
148
149   private boolean isAfter(Violation violation, Date date) {
150     if (date == null) {
151       return true;
152     }
153     return violation.getCreatedAt() != null && violation.getCreatedAt().after(date);
154   }
155
156 }