View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2026 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.maven;
6   
7   import com.qulice.spi.ResourceValidator;
8   import java.io.File;
9   import java.util.Collection;
10  import java.util.LinkedHashMap;
11  import java.util.Locale;
12  import java.util.Map;
13  import java.util.stream.Collectors;
14  
15  /**
16   * The scope of a finished check, as one line of text.
17   *
18   * <p>Tells the reader what the run has just covered: how many Java files
19   * went through the validators and how many rules judged them, validator
20   * by validator. A validator that cannot count its rules stays out of the
21   * breakdown, instead of claiming zero, and a module that had no file to
22   * give them counts no rules at all.</p>
23   *
24   * @since 1.0
25   */
26  final class Summary {
27  
28      /**
29       * Files handed to the validators.
30       */
31      private final Collection<File> files;
32  
33      /**
34       * Validators that read them.
35       */
36      private final Collection<ResourceValidator> validators;
37  
38      /**
39       * Constructor.
40       * @param files Files handed to the validators
41       * @param validators Validators that read them
42       */
43      Summary(final Collection<File> files,
44          final Collection<ResourceValidator> validators) {
45          this.files = files;
46          this.validators = validators;
47      }
48  
49      @Override
50      public String toString() {
51          final Map<String, Integer> counts = this.counts();
52          return String.format(
53              "checked %s against %d rules%s",
54              this.javas(),
55              counts.values().stream().mapToInt(Integer::intValue).sum(),
56              Summary.breakdown(counts)
57          );
58      }
59  
60      /**
61       * How many rules each validator applies, by validator name, leaving
62       * out the ones that count none.
63       *
64       * <p>A module without a single file to read runs no validator at all,
65       * so nobody is asked to count: the counting itself is expensive, since
66       * it makes Checkstyle load its configuration and PMD resolve its
67       * ruleset.</p>
68       *
69       * @return Rule counts, in the order the validators ran
70       */
71      private Map<String, Integer> counts() {
72          final Map<String, Integer> counts =
73              new LinkedHashMap<>(this.validators.size());
74          if (!this.files.isEmpty()) {
75              for (final ResourceValidator validator : this.validators) {
76                  final int rules = validator.rules();
77                  if (rules > 0) {
78                      counts.put(validator.name(), rules);
79                  }
80              }
81          }
82          return counts;
83      }
84  
85      /**
86       * The Java files among them, counted and named.
87       * @return Text, e.g. {@code "42 .java files"}
88       */
89      private String javas() {
90          final long count = this.files.stream().filter(
91              file -> file.getName().toLowerCase(Locale.ROOT).endsWith(".java")
92          ).count();
93          final String text;
94          if (count == 1L) {
95              text = "1 .java file";
96          } else {
97              text = String.format("%d .java files", count);
98          }
99          return text;
100     }
101 
102     /**
103      * Spell the rule counts out, validator by validator.
104      * @param counts Rule counts by validator name
105      * @return Text in brackets, e.g. {@code " (253 Checkstyle, 132 PMD)"},
106      *  or empty when nobody counted anything
107      */
108     private static String breakdown(final Map<String, Integer> counts) {
109         final String text;
110         if (counts.isEmpty()) {
111             text = "";
112         } else {
113             text = counts.entrySet().stream().map(
114                 entry -> String.format("%d %s", entry.getValue(), entry.getKey())
115             ).collect(Collectors.joining(", ", " (", ")"));
116         }
117         return text;
118     }
119 }