View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2025 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.pmd;
6   
7   import com.jcabi.log.Logger;
8   import com.qulice.spi.Environment;
9   import java.io.File;
10  import java.nio.charset.Charset;
11  import java.util.Collection;
12  import java.util.Collections;
13  import java.util.LinkedList;
14  import net.sourceforge.pmd.PMDConfiguration;
15  import net.sourceforge.pmd.Report;
16  import net.sourceforge.pmd.RuleContext;
17  import net.sourceforge.pmd.RulePriority;
18  import net.sourceforge.pmd.util.datasource.DataSource;
19  
20  /**
21   * Validates source files via <code>PmdValidator</code>.
22   *
23   * @since 0.3
24   */
25  @SuppressWarnings("deprecation")
26  final class SourceValidator {
27      /**
28       * Rule context.
29       */
30      private final RuleContext context;
31  
32      /**
33       * Report listener.
34       */
35      private final PmdListener listener;
36  
37      /**
38       * Report renderer (responsible for picking up additional
39       * PMD-generated reports with processing errors).
40       */
41      private final PmdRenderer renderer;
42  
43      /**
44       * Rules.
45       */
46      private final PMDConfiguration config;
47  
48      /**
49       * Source files encoding.
50       */
51      private final Charset encoding;
52  
53      /**
54       * Creates new instance of <code>SourceValidator</code>.
55       * @param env Environment
56       */
57      SourceValidator(final Environment env) {
58          this.context = new RuleContext();
59          this.listener = new PmdListener(env);
60          this.renderer = new PmdRenderer();
61          this.config = new PMDConfiguration();
62          this.encoding = env.encoding();
63      }
64  
65      /**
66       * Performs validation of the input source files.
67       * @param sources Input source files.
68       * @param path Base path.
69       * @return Collection of violations.
70       */
71      @SuppressWarnings({"PMD.AvoidInstantiatingObjectsInLoops", "PMD.CloseResource"})
72      public Collection<PmdError> validate(
73          final Collection<DataSource> sources, final String path) {
74          this.config.setRuleSets("com/qulice/pmd/ruleset.xml");
75          this.config.setThreads(0);
76          this.config.setMinimumPriority(RulePriority.LOW);
77          this.config.setIgnoreIncrementalAnalysis(true);
78          this.config.setShowSuppressedViolations(true);
79          this.config.setSourceEncoding(this.encoding.name());
80          final Report report = new Report();
81          report.addListener(this.listener);
82          this.context.setReport(report);
83          for (final DataSource source : sources) {
84              final String name = source.getNiceFileName(false, path);
85              Logger.debug(this, "Processing file: %s", name);
86              this.context.setSourceCodeFile(new File(name));
87              this.validateOne(source);
88          }
89          this.renderer.exportTo(report);
90          report.errors().forEachRemaining(this.listener::onProcessingError);
91          report.configErrors().forEachRemaining(this.listener::onConfigError);
92          Logger.debug(
93              this,
94              "got %d errors",
95              this.listener.errors().size()
96          );
97          return this.listener.errors();
98      }
99  
100     /**
101      * Performs validation of one file.
102      * @param source Input source file
103      */
104     private void validateOne(final DataSource source) {
105         final net.sourceforge.pmd.RuleSetFactory factory =
106             new net.sourceforge.pmd.RuleSetFactory(
107                 new net.sourceforge.pmd.util.ResourceLoader(),
108                 RulePriority.LOW,
109                 false,
110                 true
111             );
112         net.sourceforge.pmd.PMD.processFiles(
113             this.config,
114             factory,
115             new LinkedList<>(Collections.singleton(source)),
116             this.context,
117             Collections.singletonList(this.renderer)
118         );
119     }
120 }