]> source.dussan.org Git - sonarqube.git/blob
505e53851b3a1ca411230e47253df2af00a137ce
[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 java.util.List;
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 List<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 = List.of(
62       new DocumentationFormula(),
63       new CommentDensityFormula()
64     );
65   }
66
67   @Override
68   public void execute(ComputationStep.Context context) {
69     new PathAwareCrawler<>(
70       FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository).buildFor(formulas))
71       .visit(treeRootHolder.getRoot());
72   }
73
74   private class CommentDensityFormula implements Formula<IntSumCounter> {
75
76     private final Metric nclocMetric;
77
78     public CommentDensityFormula() {
79       this.nclocMetric = metricRepository.getByKey(NCLOC_KEY);
80     }
81
82     @Override
83     public IntSumCounter createNewCounter() {
84       return new IntSumCounter(COMMENT_LINES_KEY);
85     }
86
87     @Override
88     public Optional<Measure> createMeasure(IntSumCounter counter, CreateMeasureContext context) {
89       Optional<Measure> measure = createCommentLinesMeasure(counter, context);
90       return measure.isPresent() ? measure : createCommentLinesDensityMeasure(counter, context);
91     }
92
93     private Optional<Measure> createCommentLinesMeasure(SumCounter counter, CreateMeasureContext context) {
94       Optional<Integer> commentLines = counter.getValue();
95       if (COMMENT_LINES_KEY.equals(context.getMetric().getKey())
96         && commentLines.isPresent()
97         && CrawlerDepthLimit.LEAVES.isDeeperThan(context.getComponent().getType())) {
98         return Optional.of(Measure.newMeasureBuilder().create(commentLines.get()));
99       }
100       return Optional.empty();
101     }
102
103     private Optional<Measure> createCommentLinesDensityMeasure(SumCounter counter, CreateMeasureContext context) {
104       if (COMMENT_LINES_DENSITY_KEY.equals(context.getMetric().getKey())) {
105         Optional<Measure> nclocsOpt = measureRepository.getRawMeasure(context.getComponent(), nclocMetric);
106         Optional<Integer> commentsOpt = counter.getValue();
107         if (nclocsOpt.isPresent() && commentsOpt.isPresent()) {
108           double nclocs = nclocsOpt.get().getIntValue();
109           double comments = commentsOpt.get();
110           double divisor = nclocs + comments;
111           if (divisor > 0d) {
112             double value = 100d * (comments / divisor);
113             return Optional.of(Measure.newMeasureBuilder().create(value, context.getMetric().getDecimalScale()));
114           }
115         }
116       }
117       return Optional.empty();
118     }
119
120     @Override
121     public String[] getOutputMetricKeys() {
122       return new String[] {COMMENT_LINES_KEY, COMMENT_LINES_DENSITY_KEY};
123     }
124   }
125
126   private static class DocumentationFormula implements Formula<DocumentationCounter> {
127
128     @Override
129     public DocumentationCounter createNewCounter() {
130       return new DocumentationCounter();
131     }
132
133     @Override
134     public Optional<Measure> createMeasure(DocumentationCounter counter, CreateMeasureContext context) {
135       Optional<Measure> measure = getMeasure(context, counter.getPublicApiValue(), PUBLIC_API_KEY);
136       if (measure.isPresent()) {
137         return measure;
138       }
139       measure = getMeasure(context, counter.getPublicUndocumentedApiValue(), PUBLIC_UNDOCUMENTED_API_KEY);
140       return measure.isPresent() ? measure : getDensityMeasure(counter, context);
141     }
142
143     private static Optional<Measure> getMeasure(CreateMeasureContext context, Optional<Integer> metricValue, String metricKey) {
144       if (context.getMetric().getKey().equals(metricKey) && metricValue.isPresent()
145         && CrawlerDepthLimit.LEAVES.isDeeperThan(context.getComponent().getType())) {
146         return Optional.of(Measure.newMeasureBuilder().create(metricValue.get()));
147       }
148       return Optional.empty();
149     }
150
151     private static Optional<Measure> getDensityMeasure(DocumentationCounter counter, CreateMeasureContext context) {
152       if (context.getMetric().getKey().equals(PUBLIC_DOCUMENTED_API_DENSITY_KEY) && counter.getPublicApiValue().isPresent()
153         && counter.getPublicUndocumentedApiValue().isPresent()) {
154         double publicApis = counter.getPublicApiValue().get();
155         double publicUndocumentedApis = counter.getPublicUndocumentedApiValue().get();
156         if (publicApis > 0d) {
157           double documentedAPI = publicApis - publicUndocumentedApis;
158           double value = 100d * (documentedAPI / publicApis);
159           return Optional.of(Measure.newMeasureBuilder().create(value, context.getMetric().getDecimalScale()));
160         }
161       }
162       return Optional.empty();
163     }
164
165     @Override
166     public String[] getOutputMetricKeys() {
167       return new String[] {PUBLIC_API_KEY, PUBLIC_UNDOCUMENTED_API_KEY, PUBLIC_DOCUMENTED_API_DENSITY_KEY};
168     }
169   }
170
171   private static class DocumentationCounter implements Counter<DocumentationCounter> {
172
173     private final SumCounter publicApiCounter;
174     private final SumCounter publicUndocumentedApiCounter;
175
176     public DocumentationCounter() {
177       this.publicApiCounter = new IntSumCounter(PUBLIC_API_KEY);
178       this.publicUndocumentedApiCounter = new IntSumCounter(PUBLIC_UNDOCUMENTED_API_KEY);
179     }
180
181     @Override
182     public void aggregate(DocumentationCounter counter) {
183       publicApiCounter.aggregate(counter.publicApiCounter);
184       publicUndocumentedApiCounter.aggregate(counter.publicUndocumentedApiCounter);
185     }
186
187     @Override
188     public void initialize(CounterInitializationContext context) {
189       publicApiCounter.initialize(context);
190       publicUndocumentedApiCounter.initialize(context);
191     }
192
193     public Optional<Integer> getPublicApiValue() {
194       return publicApiCounter.getValue();
195     }
196
197     public Optional<Integer> getPublicUndocumentedApiValue() {
198       return publicUndocumentedApiCounter.getValue();
199     }
200   }
201
202   @Override
203   public String getDescription() {
204     return "Compute comment measures";
205   }
206 }