View Javadoc
1   /*
2    * Copyright (c) 2011-2024 Qulice.com
3    *
4    * All rights reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions
8    * are met: 1) Redistributions of source code must retain the above
9    * copyright notice, this list of conditions and the following
10   * disclaimer. 2) Redistributions in binary form must reproduce the above
11   * copyright notice, this list of conditions and the following
12   * disclaimer in the documentation and/or other materials provided
13   * with the distribution. 3) Neither the name of the Qulice.com nor
14   * the names of its contributors may be used to endorse or promote
15   * products derived from this software without specific prior written
16   * permission.
17   *
18   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
20   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29   * OF THE POSSIBILITY OF SUCH DAMAGE.
30   */
31  package com.qulice.pmd;
32  
33  import com.jcabi.log.Logger;
34  import com.qulice.spi.Environment;
35  import java.io.File;
36  import java.nio.charset.Charset;
37  import java.util.Collection;
38  import java.util.Collections;
39  import java.util.LinkedList;
40  import net.sourceforge.pmd.PMDConfiguration;
41  import net.sourceforge.pmd.Report;
42  import net.sourceforge.pmd.RuleContext;
43  import net.sourceforge.pmd.RulePriority;
44  import net.sourceforge.pmd.util.datasource.DataSource;
45  
46  /**
47   * Validates source files via <code>PmdValidator</code>.
48   *
49   * @since 0.3
50   */
51  @SuppressWarnings("deprecation")
52  final class SourceValidator {
53      /**
54       * Rule context.
55       */
56      private final RuleContext context;
57  
58      /**
59       * Report listener.
60       */
61      private final PmdListener listener;
62  
63      /**
64       * Report renderer (responsible for picking up additional
65       * PMD-generated reports with processing errors).
66       */
67      private final PmdRenderer renderer;
68  
69      /**
70       * Rules.
71       */
72      private final PMDConfiguration config;
73  
74      /**
75       * Source files encoding.
76       */
77      private final Charset encoding;
78  
79      /**
80       * Creates new instance of <code>SourceValidator</code>.
81       * @param env Environment
82       */
83      SourceValidator(final Environment env) {
84          this.context = new RuleContext();
85          this.listener = new PmdListener(env);
86          this.renderer = new PmdRenderer();
87          this.config = new PMDConfiguration();
88          this.encoding = env.encoding();
89      }
90  
91      /**
92       * Performs validation of the input source files.
93       * @param sources Input source files.
94       * @param path Base path.
95       * @return Collection of violations.
96       */
97      @SuppressWarnings({"PMD.AvoidInstantiatingObjectsInLoops", "PMD.CloseResource"})
98      public Collection<PmdError> validate(
99          final Collection<DataSource> sources, final String path) {
100         this.config.setRuleSets("com/qulice/pmd/ruleset.xml");
101         this.config.setThreads(0);
102         this.config.setMinimumPriority(RulePriority.LOW);
103         this.config.setIgnoreIncrementalAnalysis(true);
104         this.config.setShowSuppressedViolations(true);
105         this.config.setSourceEncoding(this.encoding.name());
106         final Report report = new Report();
107         report.addListener(this.listener);
108         this.context.setReport(report);
109         for (final DataSource source : sources) {
110             final String name = source.getNiceFileName(false, path);
111             Logger.debug(this, "Processing file: %s", name);
112             this.context.setSourceCodeFile(new File(name));
113             this.validateOne(source);
114         }
115         this.renderer.exportTo(report);
116         report.errors().forEachRemaining(this.listener::onProcessingError);
117         report.configErrors().forEachRemaining(this.listener::onConfigError);
118         Logger.debug(
119             this,
120             "got %d errors",
121             this.listener.errors().size()
122         );
123         return this.listener.errors();
124     }
125 
126     /**
127      * Performs validation of one file.
128      * @param source Input source file
129      */
130     private void validateOne(final DataSource source) {
131         final net.sourceforge.pmd.RuleSetFactory factory =
132             new net.sourceforge.pmd.RuleSetFactory(
133                 new net.sourceforge.pmd.util.ResourceLoader(),
134                 RulePriority.LOW,
135                 false,
136                 true
137             );
138         net.sourceforge.pmd.PMD.processFiles(
139             this.config,
140             factory,
141             new LinkedList<>(Collections.singleton(source)),
142             this.context,
143             Collections.singletonList(this.renderer)
144         );
145     }
146 }