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 long startLoadRules = System.nanoTime();
218 RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.getRulesetFactory(configuration);
219
220 RuleSets ruleSets = RulesetsFactoryUtils.getRuleSets(configuration.getRuleSets(), ruleSetFactory,
221 startLoadRules);
222 if (ruleSets == null)
223 return;
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
241 processFiles(configuration, ruleSetFactory, files, ctx, renderers);
242
243 reportStart = System.nanoTime();
244 renderer.end();
245 renderer.flush();
246 } catch (Exception e) {
247 String message = e.getMessage();
248 if (message != null) {
249 LOG.severe(message);
250 } else {
251 LOG.log(Level.SEVERE, "Exception during processing", e);
252 }
253 LOG.log(Level.FINE, "Exception during processing", e);
254 LOG.info(PMDCommandLineInterface.buildUsageText());
255 } finally {
256 Benchmarker.mark(Benchmark.Reporting, System.nanoTime() - reportStart, 0);
257 }
258 }
259
260
261
262
263
264
265
266
267 public static RuleContext newRuleContext(String sourceCodeFilename, File sourceCodeFile) {
268
269 RuleContext context = new RuleContext();
270 context.setSourceCodeFile(sourceCodeFile);
271 context.setSourceCodeFilename(sourceCodeFilename);
272 context.setReport(new Report());
273 return context;
274 }
275
276
277
278
279
280
281
282 public interface ProgressMonitor {
283
284
285
286
287
288
289
290
291
292 boolean status(int total, int totalDone);
293 }
294
295
296
297
298
299
300
301
302
303
304
305
306 public static void processFiles(PMDConfiguration configuration, RuleSetFactory ruleSetFactory,
307 Collection<File> files, RuleContext ctx, ProgressMonitor monitor) {
308
309
310
311
312 }
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329 public static void processFiles(final PMDConfiguration configuration, final RuleSetFactory ruleSetFactory,
330 final List<DataSource> files, final RuleContext ctx, final List<Renderer> renderers) {
331
332 sortFiles(configuration, files);
333
334
335
336
337
338
339 if (SystemUtils.MT_SUPPORTED && configuration.getThreads() > 0) {
340 new MultiThreadProcessor(configuration).processFiles(ruleSetFactory, files, ctx, renderers);
341 } else {
342 new MonoThreadProcessor(configuration).processFiles(ruleSetFactory, files, ctx, renderers);
343 }
344 }
345
346 private static void sortFiles(final PMDConfiguration configuration, final List<DataSource> files) {
347 if (configuration.isStressTest()) {
348
349 Collections.shuffle(files);
350 } else {
351 final boolean useShortNames = configuration.isReportShortNames();
352 final String inputPaths = configuration.getInputPaths();
353 Collections.sort(files, new Comparator<DataSource>() {
354 public int compare(DataSource left, DataSource right) {
355 String leftString = left.getNiceFileName(useShortNames, inputPaths);
356 String rightString = right.getNiceFileName(useShortNames, inputPaths);
357 return leftString.compareTo(rightString);
358 }
359 });
360 }
361 }
362
363
364
365
366
367
368
369 public static List<DataSource> getApplicableFiles(PMDConfiguration configuration, Set<Language> languages) {
370 long startFiles = System.nanoTime();
371 LanguageFilenameFilter fileSelector = new LanguageFilenameFilter(languages);
372 List<DataSource> files = new ArrayList<DataSource>();
373
374 if (null != configuration.getInputPaths()) {
375 files.addAll(FileUtil.collectFiles(configuration.getInputPaths(), fileSelector));
376 }
377
378 if (null != configuration.getInputUri()) {
379 String uriString = configuration.getInputUri();
380 try {
381 List<DataSource> dataSources = getURIDataSources(uriString);
382
383 files.addAll(dataSources);
384 } catch (PMDException ex) {
385 LOG.log(Level.SEVERE, "Problem with Input URI", ex);
386 throw new RuntimeException("Problem with DBURI: " + uriString, ex);
387 }
388 }
389 long endFiles = System.nanoTime();
390 Benchmarker.mark(Benchmark.CollectFiles, endFiles - startFiles, 0);
391 return files;
392 }
393
394 private static Set<Language> getApplicableLanguages(PMDConfiguration configuration, RuleSets ruleSets) {
395 Set<Language> languages = new HashSet<Language>();
396 LanguageVersionDiscoverer discoverer = configuration.getLanguageVersionDiscoverer();
397
398 for (Rule rule : ruleSets.getAllRules()) {
399 Language language = rule.getLanguage();
400 if (languages.contains(language))
401 continue;
402 LanguageVersion version = discoverer.getDefaultLanguageVersion(language);
403 if (RuleSet.applies(rule, version)) {
404 languages.add(language);
405 LOG.fine("Using " + language.getShortName() + " version: " + version.getShortName());
406 }
407 }
408 return languages;
409 }
410
411
412
413
414
415
416 public static void main(String[] args) {
417 PMDCommandLineInterface.run(args);
418 }
419
420
421
422
423
424
425 public static int run(String[] args) {
426 int status = 0;
427 long start = System.nanoTime();
428 final PMDParameters params = PMDCommandLineInterface.extractParameters(new PMDParameters(), args, "pmd");
429 final PMDConfiguration configuration = PMDParameters.transformParametersIntoConfiguration(params);
430
431 final Level logLevel = params.isDebug() ? Level.FINER : Level.INFO;
432 final Handler logHandler = new ConsoleLogHandler();
433 final ScopedLogHandlersManager logHandlerManager = new ScopedLogHandlersManager(logLevel, logHandler);
434 final Level oldLogLevel = LOG.getLevel();
435 LOG.setLevel(logLevel);
436
437 try {
438 PMD.doPMD(configuration);
439 } catch (Exception e) {
440 PMDCommandLineInterface.buildUsageText();
441 System.out.println(e.getMessage());
442 status = PMDCommandLineInterface.ERROR_STATUS;
443 } finally {
444 logHandlerManager.close();
445 LOG.setLevel(oldLogLevel);
446 if (params.isBenchmark()) {
447 long end = System.nanoTime();
448 Benchmarker.mark(Benchmark.TotalPMD, end - start, 0);
449
450 TextReport report = new TextReport();
451
452
453 report.generate(Benchmarker.values(), System.err);
454 }
455 }
456 return status;
457 }
458
459
460
461
462 public static final String VERSION;
463
464
465
466 static {
467 String pmdVersion = null;
468 InputStream stream = PMD.class.getResourceAsStream("/META-INF/maven/net.sourceforge.pmd/pmd/pom.properties");
469 if (stream != null) {
470 try {
471 Properties properties = new Properties();
472 properties.load(stream);
473 pmdVersion = properties.getProperty("version");
474 } catch (IOException e) {
475 LOG.log(Level.FINE, "Couldn't determine version of PMD", e);
476 }
477 }
478 if (pmdVersion == null) {
479 pmdVersion = "unknown";
480 }
481 VERSION = pmdVersion;
482 }
483 }