]> source.dussan.org Git - sonarqube.git/blob
290dab0f08537d9204bc24977a4494c16e5e0433
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2021 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program 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  * This program 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.ce.task.projectanalysis.step;
21
22 import com.google.common.collect.ImmutableList;
23 import java.util.Optional;
24 import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit;
25 import org.sonar.ce.task.projectanalysis.component.PathAwareCrawler;
26 import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
27 import org.sonar.ce.task.projectanalysis.formula.Counter;
28 import org.sonar.ce.task.projectanalysis.formula.CounterInitializationContext;
29 import org.sonar.ce.task.projectanalysis.formula.CreateMeasureContext;
30 import org.sonar.ce.task.projectanalysis.formula.Formula;
31 import org.sonar.ce.task.projectanalysis.formula.FormulaExecutorComponentVisitor;
32 import org.sonar.ce.task.projectanalysis.formula.counter.IntSumCounter;
33 import org.sonar.ce.task.projectanalysis.formula.counter.SumCounter;
34 import org.sonar.ce.task.projectanalysis.measure.Measure;
35 import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
36 import org.sonar.ce.task.projectanalysis.metric.Metric;
37 import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
38 import org.sonar.ce.task.step.ComputationStep;
39
40 import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES_DENSITY_KEY;
41 import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES_KEY;
42 import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
43 import static org.sonar.api.measures.CoreMetrics.PUBLIC_API_KEY;
44 import static org.sonar.api.measures.CoreMetrics.PUBLIC_DOCUMENTED_API_DENSITY_KEY;
45 import static org.sonar.api.measures.CoreMetrics.PUBLIC_UNDOCUMENTED_API_KEY;
46
47 /**
48  * Computes comments measures on files and then aggregates them on higher components.
49  */
50 public class CommentMeasuresStep implements ComputationStep {
51
52   private final TreeRootHolder treeRootHolder;
53   private final MetricRepository metricRepository;
54   private final MeasureRepository measureRepository;
55   private final ImmutableList<Formula> formulas;
56
57   public CommentMeasuresStep(TreeRootHolder treeRootHolder, MetricRepository metricRepository, MeasureRepository measureRepository) {
58     this.treeRootHolder = treeRootHolder;
59     this.metricRepository = metricRepository;
60     this.measureRepository = measureRepository;
61     this.formulas = ImmutableList.of(
62       new DocumentationFormula(),
63       new CommentDensityFormula());
64   }
65
66   @Override
67   public void execute(ComputationStep.Context context) {
68     new PathAwareCrawler<>(
69       FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository).buildFor(formulas))
70       .visit(treeRootHolder.getRoot());
71   }
72
73   private class CommentDensityFormula implements Formula<IntSumCounter> {
74
75     private final Metric nclocMetric;
76
77     public CommentDensityFormula() {
78       this.nclocMetric = metricRepository.getByKey(NCLOC_KEY);
79     }
80
81     @Override
82     public IntSumCounter createNewCounter() {
83       return new IntSumCounter(COMMENT_LINES_KEY);
84     }
85
86     @Override
87     public Optional<Measure> createMeasure(IntSumCounter counter, CreateMeasureContext context) {
88       Optional<Measure> measure = createCommentLinesMeasure(counter, context);
89       return measure.isPresent() ? measure : createCommentLinesDensityMeasure(counter, context);
90     }
91
92     private Optional<Measure> createCommentLinesMeasure(SumCounter counter, CreateMeasureContext context) {
93       Optional<Integer> commentLines = counter.getValue();
94       if (COMMENT_LINES_KEY.equals(context.getMetric().getKey())
95         && commentLines.isPresent()
96         && CrawlerDepthLimit.LEAVES.isDeeperThan(context.getComponent().getType())) {
97         return Optional.of(Measure.newMeasureBuilder().create(commentLines.get()));
98       }
99       return Optional.empty();
100     }
101
102     private Optional<Measure> createCommentLinesDensityMeasure(SumCounter counter, CreateMeasureContext context) {
103       if (COMMENT_LINES_DENSITY_KEY.equals(context.getMetric().getKey())) {
104         Optional<Measure> nclocsOpt = measureRepository.getRawMeasure(context.getComponent(), nclocMetric);
105         Optional<Integer> commentsOpt = counter.getValue();
106         if (nclocsOpt.isPresent() && commentsOpt.isPresent()) {
107           double nclocs = nclocsOpt.get().getIntValue();
108           double comments = commentsOpt.get();
109           double divisor = nclocs + comments;
110           if (divisor > 0d) {
111             double value = 100d * (comments / divisor);
112             return Optional.of(Measure.newMeasureBuilder().create(value, context.getMetric().getDecimalScale()));
113           }
114         }
115       }
116       return Optional.empty();
117     }
118
119     @Override
120     public String[] getOutputMetricKeys() {
121       return new String[] {COMMENT_LINES_KEY, COMMENT_LINES_DENSITY_KEY};
122     }
123   }
124
125   private static class DocumentationFormula implements Formula<DocumentationCounter> {
126
127     @Override
128     public DocumentationCounter createNewCounter() {
129       return new DocumentationCounter();
130     }
131
132     @Override
133     public Optional<Measure> createMeasure(DocumentationCounter counter, CreateMeasureContext context) {
134       Optional<Measure> measure = getMeasure(context, counter.getPublicApiValue(), PUBLIC_API_KEY);
135       if (measure.isPresent()) {
136         return measure;
137       }
138       measure = getMeasure(context, counter.getPublicUndocumentedApiValue(), PUBLIC_UNDOCUMENTED_API_KEY);
139       return measure.isPresent() ? measure : getDensityMeasure(counter, context);
140     }
141
142     private static Optional<Measure> getMeasure(CreateMeasureContext context, Optional<Integer> metricValue, String metricKey) {
143       if (context.getMetric().getKey().equals(metricKey) && metricValue.isPresent()
144         && CrawlerDepthLimit.LEAVES.isDeeperThan(context.getComponent().getType())) {
145         return Optional.of(Measure.newMeasureBuilder().create(metricValue.get()));
146       }
147       return Optional.empty();
148     }
149
150     private static Optional<Measure> getDensityMeasure(DocumentationCounter counter, CreateMeasureContext context) {
151       if (context.getMetric().getKey().equals(PUBLIC_DOCUMENTED_API_DENSITY_KEY) && counter.getPublicApiValue().isPresent()
152         && counter.getPublicUndocumentedApiValue().isPresent()) {
153         double publicApis = counter.getPublicApiValue().get();
154         double publicUndocumentedApis = counter.getPublicUndocumentedApiValue().get();
155         if (publicApis > 0d) {
156           double documentedAPI = publicApis - publicUndocumentedApis;
157           double value = 100d * (documentedAPI / publicApis);
158           return Optional.of(Measure.newMeasureBuilder().create(value, context.getMetric().getDecimalScale()));
159         }
160       }
161       return Optional.empty();
162     }
163
164     @Override
165     public String[] getOutputMetricKeys() {
166       return new String[] {PUBLIC_API_KEY, PUBLIC_UNDOCUMENTED_API_KEY, PUBLIC_DOCUMENTED_API_DENSITY_KEY};
167     }
168   }
169
170   private static class DocumentationCounter implements Counter<DocumentationCounter> {
171
172     private final SumCounter publicApiCounter;
173     private final SumCounter publicUndocumentedApiCounter;
174
175     public DocumentationCounter() {
176       this.publicApiCounter = new IntSumCounter(PUBLIC_API_KEY);
177       this.publicUndocumentedApiCounter = new IntSumCounter(PUBLIC_UNDOCUMENTED_API_KEY);
178     }
179
180     @Override
181     public void aggregate(DocumentationCounter counter) {
182       publicApiCounter.aggregate(counter.publicApiCounter);
183       publicUndocumentedApiCounter.aggregate(counter.publicUndocumentedApiCounter);
184     }
185
186     @Override
187     public void initialize(CounterInitializationContext context) {
188       publicApiCounter.initialize(context);
189       publicUndocumentedApiCounter.initialize(context);
190     }
191
192     public Optional<Integer> getPublicApiValue() {
193       return publicApiCounter.getValue();
194     }
195
196     public Optional<Integer> getPublicUndocumentedApiValue() {
197       return publicUndocumentedApiCounter.getValue();
198     }
199   }
200
201   @Override
202   public String getDescription() {
203     return "Compute comment measures";
204   }
205 }