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