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.net.URISyntaxException;
10 import java.sql.SQLException;
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.Comparator;
15 import java.util.HashSet;
16 import java.util.LinkedList;
17 import java.util.List;
18 import java.util.Properties;
19 import java.util.Set;
20 import java.util.concurrent.atomic.AtomicInteger;
21 import java.util.logging.Handler;
22 import java.util.logging.Level;
23 import java.util.logging.Logger;
24
25 import net.sourceforge.pmd.benchmark.Benchmark;
26 import net.sourceforge.pmd.benchmark.Benchmarker;
27 import net.sourceforge.pmd.benchmark.TextReport;
28 import net.sourceforge.pmd.cli.PMDCommandLineInterface;
29 import net.sourceforge.pmd.cli.PMDParameters;
30 import net.sourceforge.pmd.lang.*;
31 import net.sourceforge.pmd.processor.MonoThreadProcessor;
32 import net.sourceforge.pmd.processor.MultiThreadProcessor;
33 import net.sourceforge.pmd.renderers.Renderer;
34 import net.sourceforge.pmd.stat.Metric;
35 import net.sourceforge.pmd.util.FileUtil;
36 import net.sourceforge.pmd.util.IOUtil;
37 import net.sourceforge.pmd.util.SystemUtils;
38 import net.sourceforge.pmd.util.database.DBMSMetadata;
39 import net.sourceforge.pmd.util.database.DBURI;
40 import net.sourceforge.pmd.util.database.SourceObject;
41 import net.sourceforge.pmd.util.datasource.DataSource;
42 import net.sourceforge.pmd.util.datasource.ReaderDataSource;
43 import net.sourceforge.pmd.util.log.ConsoleLogHandler;
44 import net.sourceforge.pmd.util.log.ScopedLogHandlersManager;
45
46
47
48
49
50
51
52 public class PMD {
53
54 private static final Logger LOG = Logger.getLogger(PMD.class.getName());
55
56
57 public static final String EOL = System.getProperty("line.separator", "\n");
58
59
60 public static final String SUPPRESS_MARKER = "NOPMD";
61
62
63
64
65
66
67
68
69 public static List<DataSource> getURIDataSources(String uriString) throws PMDException {
70 List<DataSource> dataSources = new ArrayList<DataSource>();
71
72 try {
73 DBURI dbUri = new DBURI(uriString);
74 DBMSMetadata dbmsMetadata = new DBMSMetadata(dbUri);
75 LOG.log(Level.FINE, "DBMSMetadata retrieved");
76 List<SourceObject> sourceObjectList = dbmsMetadata.getSourceObjectList();
77 LOG.log(Level.FINE, "Located {0} database source objects", sourceObjectList.size());
78 for (SourceObject sourceObject : sourceObjectList) {
79 String falseFilePath = sourceObject.getPseudoFileName();
80 LOG.log(Level.FINEST, "Adding database source object {0}", falseFilePath);
81
82 try {
83 dataSources.add(new ReaderDataSource(dbmsMetadata.getSourceCode(sourceObject), falseFilePath));
84 } catch (SQLException ex) {
85 if (LOG.isLoggable(Level.WARNING)) {
86 LOG.log(Level.WARNING, "Cannot get SourceCode for " + falseFilePath + " - skipping ...", ex);
87 }
88 }
89 }
90 } catch (URISyntaxException e) {
91 throw new PMDException("Cannot get DataSources from DBURI - \"" + uriString + "\"", e);
92 } catch (SQLException e) {
93 throw new PMDException("Cannot get DataSources from DBURI, couldn't access the database - \"" + uriString
94 + "\"", e);
95 } catch (ClassNotFoundException e) {
96 throw new PMDException("Cannot get DataSources from DBURI, probably missing database jdbc driver - \""
97 + uriString + "\"", e);
98 } catch (Exception e) {
99 throw new PMDException("Encountered unexpected problem with URI \""
100 + uriString + "\"", e);
101 }
102 return dataSources;
103 }
104
105
106 protected final PMDConfiguration configuration;
107
108 private final SourceCodeProcessor rulesetsFileProcessor;
109
110
111
112
113
114
115
116
117 public static Parser parserFor(LanguageVersion languageVersion, PMDConfiguration configuration) {
118
119
120 LanguageVersionHandler languageVersionHandler = languageVersion.getLanguageVersionHandler();
121 ParserOptions options = languageVersionHandler.getDefaultParserOptions();
122 if (configuration != null) {
123 options.setSuppressMarker(configuration.getSuppressMarker());
124 }
125 return languageVersionHandler.getParser(options);
126 }
127
128
129
130
131
132
133
134
135
136
137 public static Report setupReport(RuleSets rs, RuleContext ctx, String fileName) {
138
139 Set<Rule> brokenRules = removeBrokenRules(rs);
140 Report report = Report.createReport(ctx, fileName);
141
142 for (Rule rule : brokenRules) {
143 report.addConfigError(new Report.RuleConfigurationError(rule, rule.dysfunctionReason()));
144 }
145
146 return report;
147 }
148
149
150
151
152
153
154
155
156
157 private static Set<Rule> removeBrokenRules(RuleSets ruleSets) {
158
159 Set<Rule> brokenRules = new HashSet<Rule>();
160 ruleSets.removeDysfunctionalRules(brokenRules);
161
162 for (Rule rule : brokenRules) {
163 if (LOG.isLoggable(Level.WARNING)) {
164 LOG.log(Level.WARNING,
165 "Removed misconfigured rule: " + rule.getName() + " cause: " + rule.dysfunctionReason());
166 }
167 }
168
169 return brokenRules;
170 }
171
172
173
174
175
176 public PMD() {
177 this(new PMDConfiguration());
178 }
179
180
181
182
183
184
185
186 public PMD(PMDConfiguration configuration) {
187 this.configuration = configuration;
188 this.rulesetsFileProcessor = new SourceCodeProcessor(configuration);
189 }
190
191
192
193
194
195
196
197
198 public PMDConfiguration getConfiguration() {
199 return configuration;
200 }
201
202
203
204
205
206 public SourceCodeProcessor getSourceCodeProcessor() {
207 return rulesetsFileProcessor;
208 }
209
210
211
212
213
214
215
216 public static int doPMD(PMDConfiguration configuration) {
217
218
219 RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.getRulesetFactory(configuration);
220 RuleSets ruleSets = RulesetsFactoryUtils.getRuleSetsWithBenchmark(configuration.getRuleSets(), ruleSetFactory);
221 if (ruleSets == null) {
222 return 0;
223 }
224
225 Set<Language> languages = getApplicableLanguages(configuration, ruleSets);
226 List<DataSource> files = getApplicableFiles(configuration, languages);
227
228 long reportStart = System.nanoTime();
229 try {
230 Renderer renderer = configuration.createRenderer();
231 List<Renderer> renderers = new LinkedList<Renderer>();
232 renderers.add(renderer);
233
234 renderer.setWriter(IOUtil.createWriter(configuration.getReportFile()));
235 renderer.start();
236
237 Benchmarker.mark(Benchmark.Reporting, System.nanoTime() - reportStart, 0);
238
239 RuleContext ctx = new RuleContext();
240 final AtomicInteger violations = new AtomicInteger(0);
241 ctx.getReport().addListener(new ReportListener() {
242 @Override
243 public void ruleViolationAdded(RuleViolation ruleViolation) {
244 violations.incrementAndGet();
245 }
246 @Override
247 public void metricAdded(Metric metric) {
248 }
249 });
250
251 processFiles(configuration, ruleSetFactory, files, ctx, renderers);
252
253 reportStart = System.nanoTime();
254 renderer.end();
255 renderer.flush();
256 return violations.get();
257 } catch (Exception e) {
258 String message = e.getMessage();
259 if (message != null) {
260 LOG.severe(message);
261 } else {
262 LOG.log(Level.SEVERE, "Exception during processing", e);
263 }
264 LOG.log(Level.FINE, "Exception during processing", e);
265 LOG.info(PMDCommandLineInterface.buildUsageText());
266 return 0;
267 } finally {
268 Benchmarker.mark(Benchmark.Reporting, System.nanoTime() - reportStart, 0);
269 }
270 }
271
272
273
274
275
276
277
278
279 public static RuleContext newRuleContext(String sourceCodeFilename, File sourceCodeFile) {
280
281 RuleContext context = new RuleContext();
282 context.setSourceCodeFile(sourceCodeFile);
283 context.setSourceCodeFilename(sourceCodeFilename);
284 context.setReport(new Report());
285 return context;
286 }
287
288
289
290
291
292
293
294 public interface ProgressMonitor {
295
296
297
298
299
300
301
302
303
304 boolean status(int total, int totalDone);
305 }
306
307
308
309
310
311
312
313
314
315
316
317
318 public static void processFiles(PMDConfiguration configuration, RuleSetFactory ruleSetFactory,
319 Collection<File> files, RuleContext ctx, ProgressMonitor monitor) {
320
321
322
323
324 }
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341 public static void processFiles(final PMDConfiguration configuration, final RuleSetFactory ruleSetFactory,
342 final List<DataSource> files, final RuleContext ctx, final List<Renderer> renderers) {
343
344 sortFiles(configuration, files);
345
346
347
348
349
350
351 if (SystemUtils.MT_SUPPORTED && configuration.getThreads() > 0) {
352 new MultiThreadProcessor(configuration).processFiles(ruleSetFactory, files, ctx, renderers);
353 } else {
354 new MonoThreadProcessor(configuration).processFiles(ruleSetFactory, files, ctx, renderers);
355 }
356 }
357
358 private static void sortFiles(final PMDConfiguration configuration, final List<DataSource> files) {
359 if (configuration.isStressTest()) {
360
361 Collections.shuffle(files);
362 } else {
363 final boolean useShortNames = configuration.isReportShortNames();
364 final String inputPaths = configuration.getInputPaths();
365 Collections.sort(files, new Comparator<DataSource>() {
366 public int compare(DataSource left, DataSource right) {
367 String leftString = left.getNiceFileName(useShortNames, inputPaths);
368 String rightString = right.getNiceFileName(useShortNames, inputPaths);
369 return leftString.compareTo(rightString);
370 }
371 });
372 }
373 }
374
375
376
377
378
379
380
381 public static List<DataSource> getApplicableFiles(PMDConfiguration configuration, Set<Language> languages) {
382 long startFiles = System.nanoTime();
383 List<DataSource> files = internalGetApplicableFiles(configuration, languages);
384 long endFiles = System.nanoTime();
385 Benchmarker.mark(Benchmark.CollectFiles, endFiles - startFiles, 0);
386 return files;
387 }
388
389 private static List<DataSource> internalGetApplicableFiles(PMDConfiguration configuration, Set<Language> languages) {
390 LanguageFilenameFilter fileSelector = new LanguageFilenameFilter(languages);
391 List<DataSource> files = new ArrayList<DataSource>();
392
393 if (null != configuration.getInputPaths()) {
394 files.addAll(FileUtil.collectFiles(configuration.getInputPaths(), fileSelector));
395 }
396
397 if (null != configuration.getInputUri()) {
398 String uriString = configuration.getInputUri();
399 try {
400 List<DataSource> dataSources = getURIDataSources(uriString);
401
402 files.addAll(dataSources);
403 } catch (PMDException ex) {
404 LOG.log(Level.SEVERE, "Problem with Input URI", ex);
405 throw new RuntimeException("Problem with DBURI: " + uriString, ex);
406 }
407 }
408 return files;
409 }
410
411 private static Set<Language> getApplicableLanguages(PMDConfiguration configuration, RuleSets ruleSets) {
412 Set<Language> languages = new HashSet<Language>();
413 LanguageVersionDiscoverer discoverer = configuration.getLanguageVersionDiscoverer();
414
415 for (Rule rule : ruleSets.getAllRules()) {
416 Language language = rule.getLanguage();
417 if (languages.contains(language)) {
418 continue;
419 }
420 LanguageVersion version = discoverer.getDefaultLanguageVersion(language);
421 if (RuleSet.applies(rule, version)) {
422 languages.add(language);
423 if (LOG.isLoggable(Level.FINE)) {
424 LOG.fine("Using " + language.getShortName() + " version: " + version.getShortName());
425 }
426 }
427 }
428 return languages;
429 }
430
431
432
433
434
435
436 public static void main(String[] args) {
437 PMDCommandLineInterface.run(args);
438 }
439
440
441
442
443
444
445
446 public static int run(String[] args) {
447 int status = 0;
448 long start = System.nanoTime();
449 final PMDParameters params = PMDCommandLineInterface.extractParameters(new PMDParameters(), args, "pmd");
450 final PMDConfiguration configuration = PMDParameters.transformParametersIntoConfiguration(params);
451
452 final Level logLevel = params.isDebug() ? Level.FINER : Level.INFO;
453 final Handler logHandler = new ConsoleLogHandler();
454 final ScopedLogHandlersManager logHandlerManager = new ScopedLogHandlersManager(logLevel, logHandler);
455 final Level oldLogLevel = LOG.getLevel();
456 LOG.setLevel(logLevel);
457
458 try {
459 int violations = PMD.doPMD(configuration);
460 if (violations > 0) {
461 status = PMDCommandLineInterface.VIOLATIONS_FOUND;
462 } else {
463 status = 0;
464 }
465 } catch (Exception e) {
466 System.out.println(PMDCommandLineInterface.buildUsageText());
467 System.out.println();
468 System.err.println(e.getMessage());
469 status = PMDCommandLineInterface.ERROR_STATUS;
470 } finally {
471 logHandlerManager.close();
472 LOG.setLevel(oldLogLevel);
473 if (params.isBenchmark()) {
474 long end = System.nanoTime();
475 Benchmarker.mark(Benchmark.TotalPMD, end - start, 0);
476
477 TextReport report = new TextReport();
478
479
480 report.generate(Benchmarker.values(), System.err);
481 }
482 }
483 return status;
484 }
485
486
487
488
489 public static final String VERSION;
490
491
492
493 static {
494 String pmdVersion = null;
495 InputStream stream = PMD.class.getResourceAsStream("/META-INF/maven/net.sourceforge.pmd/pmd-core/pom.properties");
496 if (stream != null) {
497 try {
498 Properties properties = new Properties();
499 properties.load(stream);
500 pmdVersion = properties.getProperty("version");
501 } catch (IOException e) {
502 LOG.log(Level.FINE, "Couldn't determine version of PMD", e);
503 }
504 }
505 if (pmdVersion == null) {
506 pmdVersion = "unknown";
507 }
508 VERSION = pmdVersion;
509 }
510 }