Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

CommentMeasuresStep.java 8.4KB

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