1
2
3
4 package net.sourceforge.pmd;
5
6 import java.io.File;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.util.Collection;
10 import java.util.Collections;
11 import java.util.Comparator;
12 import java.util.HashSet;
13 import java.util.LinkedList;
14 import java.util.List;
15 import java.util.Properties;
16 import java.util.Set;
17 import java.util.logging.Handler;
18 import java.util.logging.Level;
19 import java.util.logging.Logger;
20
21 import net.sourceforge.pmd.benchmark.Benchmark;
22 import net.sourceforge.pmd.benchmark.Benchmarker;
23 import net.sourceforge.pmd.benchmark.TextReport;
24 import net.sourceforge.pmd.cli.PMDCommandLineInterface;
25 import net.sourceforge.pmd.cli.PMDParameters;
26 import net.sourceforge.pmd.lang.Language;
27 import net.sourceforge.pmd.lang.LanguageFilenameFilter;
28 import net.sourceforge.pmd.lang.LanguageVersion;
29 import net.sourceforge.pmd.lang.LanguageVersionDiscoverer;
30 import net.sourceforge.pmd.lang.LanguageVersionHandler;
31 import net.sourceforge.pmd.lang.Parser;
32 import net.sourceforge.pmd.lang.ParserOptions;
33 import net.sourceforge.pmd.processor.MonoThreadProcessor;
34 import net.sourceforge.pmd.processor.MultiThreadProcessor;
35 import net.sourceforge.pmd.renderers.Renderer;
36 import net.sourceforge.pmd.util.FileUtil;
37 import net.sourceforge.pmd.util.IOUtil;
38 import net.sourceforge.pmd.util.SystemUtils;
39 import net.sourceforge.pmd.util.datasource.DataSource;
40 import net.sourceforge.pmd.util.log.ConsoleLogHandler;
41 import net.sourceforge.pmd.util.log.ScopedLogHandlersManager;
42
43
44
45
46
47
48
49 public class PMD {
50
51 private static final Logger LOG = Logger.getLogger(PMD.class.getName());
52
53 public static final String EOL = System.getProperty("line.separator", "\n");
54 public static final String SUPPRESS_MARKER = "NOPMD";
55
56 protected final PMDConfiguration configuration;
57
58 private final SourceCodeProcessor rulesetsFileProcessor;
59
60 public static Parser parserFor(LanguageVersion languageVersion, PMDConfiguration configuration) {
61
62
63 LanguageVersionHandler languageVersionHandler = languageVersion.getLanguageVersionHandler();
64 ParserOptions options = languageVersionHandler.getDefaultParserOptions();
65 if (configuration != null) options.setSuppressMarker(configuration.getSuppressMarker());
66 return languageVersionHandler.getParser(options);
67 }
68
69
70
71
72
73
74
75
76
77 public static Report setupReport(RuleSets rs, RuleContext ctx, String fileName) {
78
79 Set<Rule> brokenRules = removeBrokenRules(rs);
80 Report report = Report.createReport(ctx, fileName);
81
82 for (Rule rule : brokenRules) {
83 report.addConfigError(
84 new Report.RuleConfigurationError(rule, rule.dysfunctionReason())
85 );
86 }
87
88 return report;
89 }
90
91
92
93
94
95
96
97
98
99 private static Set<Rule> removeBrokenRules(RuleSets ruleSets) {
100
101 Set<Rule> brokenRules = new HashSet<Rule>();
102 ruleSets.removeDysfunctionalRules(brokenRules);
103
104 for (Rule rule : brokenRules) {
105 LOG.log(Level.WARNING, "Removed misconfigured rule: " + rule.getName() + " cause: " + rule.dysfunctionReason());
106 }
107
108 return brokenRules;
109 }
110
111
112
113
114
115 public PMD() {
116 this(new PMDConfiguration());
117 }
118
119
120
121
122
123
124
125 public PMD(PMDConfiguration configuration) {
126 this.configuration = configuration;
127 this.rulesetsFileProcessor = new SourceCodeProcessor(configuration);
128 }
129
130
131
132
133
134
135
136
137 public PMDConfiguration getConfiguration() {
138 return configuration;
139 }
140
141
142
143
144
145 public SourceCodeProcessor getSourceCodeProcessor() {
146 return rulesetsFileProcessor;
147 }
148
149
150
151
152
153
154 public static void doPMD(PMDConfiguration configuration) {
155
156
157 long startLoadRules = System.nanoTime();
158 RuleSetFactory ruleSetFactory = RulesetsFactoryUtils
159 .getRulesetFactory(configuration);
160
161 RuleSets ruleSets = RulesetsFactoryUtils.getRuleSets(
162 configuration.getRuleSets(), ruleSetFactory, startLoadRules);
163 if (ruleSets == null)
164 return;
165
166 Set<Language> languages = getApplicableLanguages(configuration, ruleSets);
167 List<DataSource> files = getApplicableFiles(configuration, languages);
168
169 long reportStart = System.nanoTime();
170 try {
171 Renderer renderer = configuration.createRenderer();
172 List<Renderer> renderers = new LinkedList<Renderer>();
173 renderers.add(renderer);
174
175 renderer.setWriter(IOUtil.createWriter(configuration.getReportFile()));
176 renderer.start();
177
178 Benchmarker.mark(Benchmark.Reporting, System.nanoTime() - reportStart, 0);
179
180 RuleContext ctx = new RuleContext();
181
182 processFiles(configuration, ruleSetFactory, files, ctx, renderers);
183
184 reportStart = System.nanoTime();
185 renderer.end();
186 renderer.flush();
187 } catch (Exception e) {
188 String message = e.getMessage();
189 if (message != null) {
190 LOG.severe(message);
191 } else {
192 LOG.log(Level.SEVERE, "Exception during processing", e);
193 }
194 LOG.log(Level.FINE, "Exception during processing", e);
195 LOG.info(PMDCommandLineInterface.buildUsageText());
196 } finally {
197 Benchmarker.mark(Benchmark.Reporting, System.nanoTime() - reportStart, 0);
198 }
199 }
200
201
202
203 public static RuleContext newRuleContext(String sourceCodeFilename, File sourceCodeFile) {
204
205 RuleContext context = new RuleContext();
206 context.setSourceCodeFile(sourceCodeFile);
207 context.setSourceCodeFilename(sourceCodeFilename);
208 context.setReport(new Report());
209 return context;
210 }
211
212
213
214
215
216
217
218 public interface ProgressMonitor {
219
220
221
222
223
224
225
226
227 boolean status(int total, int totalDone);
228 }
229
230
231
232
233
234
235
236
237
238
239
240 public static void processFiles(PMDConfiguration configuration,
241 RuleSetFactory ruleSetFactory, Collection<File> files,
242 RuleContext ctx, ProgressMonitor monitor) {
243
244
245
246 }
247
248
249
250
251
252
253
254
255
256
257 public static void processFiles(final PMDConfiguration configuration,
258 final RuleSetFactory ruleSetFactory, final List<DataSource> files,
259 final RuleContext ctx, final List<Renderer> renderers) {
260
261 sortFiles(configuration, files);
262
263
264
265
266
267
268 if (SystemUtils.MT_SUPPORTED && configuration.getThreads() > 0) {
269 new MultiThreadProcessor(configuration).processFiles(ruleSetFactory, files, ctx, renderers);
270 } else {
271 new MonoThreadProcessor(configuration).processFiles(ruleSetFactory, files, ctx, renderers);
272 }
273 }
274
275
276
277
278
279
280 private static void sortFiles(final PMDConfiguration configuration, final List<DataSource> files) {
281 if (configuration.isStressTest()) {
282
283 Collections.shuffle(files);
284 } else {
285 final boolean useShortNames = configuration.isReportShortNames();
286 final String inputPaths = configuration.getInputPaths();
287 Collections.sort(files, new Comparator<DataSource>() {
288 public int compare(DataSource left, DataSource right) {
289 String leftString = left.getNiceFileName(useShortNames, inputPaths);
290 String rightString = right.getNiceFileName(useShortNames, inputPaths);
291 return leftString.compareTo(rightString);
292 }
293 });
294 }
295 }
296
297
298
299
300
301
302
303 public static List<DataSource> getApplicableFiles(
304 PMDConfiguration configuration, Set<Language> languages) {
305 long startFiles = System.nanoTime();
306 LanguageFilenameFilter fileSelector = new LanguageFilenameFilter(languages);
307 List<DataSource> files = FileUtil.collectFiles(
308 configuration.getInputPaths(), fileSelector);
309 long endFiles = System.nanoTime();
310 Benchmarker.mark(Benchmark.CollectFiles, endFiles - startFiles, 0);
311 return files;
312 }
313
314
315
316
317
318
319
320 private static Set<Language> getApplicableLanguages(PMDConfiguration configuration, RuleSets ruleSets) {
321 Set<Language> languages = new HashSet<Language>();
322 LanguageVersionDiscoverer discoverer = configuration.getLanguageVersionDiscoverer();
323
324 for (Rule rule : ruleSets.getAllRules()) {
325 Language language = rule.getLanguage();
326 if (languages.contains(language))
327 continue;
328 LanguageVersion version = discoverer.getDefaultLanguageVersion(language);
329 if (RuleSet.applies(rule, version)) {
330 languages.add(language);
331 LOG.fine("Using " + language.getShortName() +
332 " version: " + version.getShortName());
333 }
334 }
335 return languages;
336 }
337
338
339
340
341
342
343 public static void main(String[] args) {
344 PMDCommandLineInterface.run(args);
345 }
346
347
348
349
350
351
352 public static int run(String[] args) {
353 int status = 0;
354 long start = System.nanoTime();
355 final PMDParameters params = PMDCommandLineInterface.extractParameters(new PMDParameters(), args, "pmd");
356 final PMDConfiguration configuration = PMDParameters.transformParametersIntoConfiguration(params);
357
358 final Level logLevel = params.isDebug() ? Level.FINER : Level.INFO;
359 final Handler logHandler = new ConsoleLogHandler();
360 final ScopedLogHandlersManager logHandlerManager = new ScopedLogHandlersManager(logLevel, logHandler);
361 final Level oldLogLevel = LOG.getLevel();
362 LOG.setLevel(logLevel);
363 try {
364 PMD.doPMD(configuration);
365 } catch (Exception e) {
366 PMDCommandLineInterface.buildUsageText();
367 System.out.println(e.getMessage());
368 status = PMDCommandLineInterface.ERROR_STATUS;
369 } finally {
370 logHandlerManager.close();
371 LOG.setLevel(oldLogLevel);
372 if (params.isBenchmark()) {
373 long end = System.nanoTime();
374 Benchmarker.mark(Benchmark.TotalPMD, end - start, 0);
375
376 TextReport report = new TextReport();
377 report.generate(Benchmarker.values(), System.err);
378 }
379 }
380 return status;
381 }
382
383 public static final String VERSION;
384
385
386
387 static {
388 String pmdVersion = null;
389 InputStream stream = PMD.class.getResourceAsStream("/META-INF/maven/net.sourceforge.pmd/pmd/pom.properties");
390 if (stream != null) {
391 try {
392 Properties properties = new Properties();
393 properties.load(stream);
394 pmdVersion = properties.getProperty("version");
395 } catch (IOException e) {
396 LOG.log(Level.FINE, "Couldn't determine version of PMD", e);
397 }
398 }
399 if (pmdVersion == null) {
400 pmdVersion = "unknown";
401 }
402 VERSION = pmdVersion;
403 }
404 }