1 /**
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd;
5
6 import java.io.BufferedInputStream;
7 import java.io.BufferedWriter;
8 import java.io.File;
9 import java.io.FileWriter;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.io.InputStreamReader;
13 import java.io.OutputStreamWriter;
14 import java.io.Reader;
15 import java.io.UnsupportedEncodingException;
16 import java.io.Writer;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Comparator;
20 import java.util.Enumeration;
21 import java.util.LinkedList;
22 import java.util.List;
23 import java.util.StringTokenizer;
24 import java.util.concurrent.Callable;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.ExecutorService;
27 import java.util.concurrent.Executors;
28 import java.util.concurrent.Future;
29 import java.util.concurrent.ThreadFactory;
30 import java.util.concurrent.atomic.AtomicInteger;
31 import java.util.logging.Handler;
32 import java.util.logging.Level;
33 import java.util.logging.Logger;
34 import java.util.zip.ZipEntry;
35 import java.util.zip.ZipFile;
36
37 import net.sourceforge.pmd.ast.CompilationUnit;
38 import net.sourceforge.pmd.ast.ParseException;
39 import net.sourceforge.pmd.cpd.SourceFileOrDirectoryFilter;
40 import net.sourceforge.pmd.parsers.Parser;
41 import net.sourceforge.pmd.renderers.Renderer;
42 import net.sourceforge.pmd.sourcetypehandlers.SourceTypeHandler;
43 import net.sourceforge.pmd.sourcetypehandlers.SourceTypeHandlerBroker;
44 import net.sourceforge.pmd.util.Benchmark;
45 import net.sourceforge.pmd.util.ClasspathClassLoader;
46 import net.sourceforge.pmd.util.ConsoleLogHandler;
47 import net.sourceforge.pmd.util.FileFinder;
48
49 public class PMD {
50 public static final String EOL = System.getProperty("line.separator", "\n");
51 public static final String VERSION = "4.2.5";
52 public static final String EXCLUDE_MARKER = "NOPMD";
53
54 private static final Logger LOG = Logger.getLogger(PMD.class.getName());
55
56 private String excludeMarker = EXCLUDE_MARKER;
57 private SourceTypeDiscoverer sourceTypeDiscoverer = new SourceTypeDiscoverer();
58 private ClassLoader classLoader = getClass().getClassLoader();
59
60 public PMD() {}
61
62 /**
63 * Processes the file read by the reader against the rule set.
64 *
65 * @param reader input stream reader
66 * @param ruleSets set of rules to process against the file
67 * @param ctx context in which PMD is operating. This contains the Renderer and
68 * whatnot
69 * @throws PMDException if the input could not be parsed or processed
70 */
71 public void processFile(Reader reader, RuleSets ruleSets, RuleContext ctx)
72 throws PMDException {
73 SourceType sourceType = getSourceTypeOfFile(ctx.getSourceCodeFilename());
74
75 processFile(reader, ruleSets, ctx, sourceType);
76 }
77
78 /**
79 * Processes the file read by the reader against the rule set.
80 *
81 * @param reader input stream reader
82 * @param ruleSets set of rules to process against the file
83 * @param ctx context in which PMD is operating. This contains the Renderer and
84 * whatnot
85 * @param sourceType the SourceType of the source
86 * @throws PMDException if the input could not be parsed or processed
87 */
88 public void processFile(Reader reader, RuleSets ruleSets, RuleContext ctx,
89 SourceType sourceType) throws PMDException {
90 try {
91
92 if (ruleSets.applies(ctx.getSourceCodeFile())) {
93 SourceTypeHandler sourceTypeHandler = SourceTypeHandlerBroker.getVisitorsFactoryForSourceType(sourceType);
94 ctx.setSourceType(sourceType);
95 Parser parser = sourceTypeHandler.getParser();
96 parser.setExcludeMarker(excludeMarker);
97 long start = System.nanoTime();
98 CompilationUnit rootNode = (CompilationUnit) parser.parse(reader);
99 ctx.excludeLines(parser.getExcludeMap());
100 long end = System.nanoTime();
101 Benchmark.mark(Benchmark.TYPE_PARSER, end - start, 0);
102 start = System.nanoTime();
103 sourceTypeHandler.getSymbolFacade().start(rootNode);
104 end = System.nanoTime();
105 Benchmark.mark(Benchmark.TYPE_SYMBOL_TABLE, end - start, 0);
106
107 Language language = SourceTypeToRuleLanguageMapper.getMappedLanguage(sourceType);
108
109 if (ruleSets.usesDFA(language)) {
110 start = System.nanoTime();
111 sourceTypeHandler.getDataFlowFacade().start(rootNode);
112 end = System.nanoTime();
113 Benchmark.mark(Benchmark.TYPE_DFA, end - start, 0);
114 }
115
116 if (ruleSets.usesTypeResolution(language)) {
117 start = System.nanoTime();
118 sourceTypeHandler.getTypeResolutionFacade(classLoader).start(rootNode);
119 end = System.nanoTime();
120 Benchmark.mark(Benchmark.TYPE_TYPE_RESOLUTION, end - start, 0);
121 }
122
123 List<CompilationUnit> acus = new ArrayList<CompilationUnit>();
124 acus.add(rootNode);
125
126 ruleSets.apply(acus, ctx, language);
127 }
128 } catch (ParseException pe) {
129 throw new PMDException("Error while parsing "
130 + ctx.getSourceCodeFilename(), pe);
131 } catch (Exception e) {
132 throw new PMDException("Error while processing "
133 + ctx.getSourceCodeFilename(), e);
134 } finally {
135 try {
136 reader.close();
137 } catch (IOException e) {}
138 }
139 }
140
141 /**
142 * Get the SourceType of the source file with given name. This depends on the fileName
143 * extension, and the java version.
144 * <p/>
145 * For compatibility with older code that does not always pass in a correct filename,
146 * unrecognized files are assumed to be java files.
147 *
148 * @param fileName Name of the file, can be absolute, or simple.
149 * @return the SourceType
150 */
151 private SourceType getSourceTypeOfFile(String fileName) {
152 SourceType sourceType = sourceTypeDiscoverer.getSourceTypeOfFile(fileName);
153 if (sourceType == null) {
154
155
156 sourceType = sourceTypeDiscoverer.getSourceTypeOfJavaFiles();
157 }
158 return sourceType;
159 }
160
161 /**
162 * Processes the file read by the reader against the rule set.
163 *
164 * @param reader input stream reader
165 * @param ruleSet set of rules to process against the file
166 * @param ctx context in which PMD is operating. This contains the Renderer and
167 * whatnot
168 * @throws PMDException if the input could not be parsed or processed
169 */
170 public void processFile(Reader reader, RuleSet ruleSet, RuleContext ctx)
171 throws PMDException {
172 processFile(reader, new RuleSets(ruleSet), ctx);
173 }
174
175 /**
176 * Processes the input stream against a rule set using the given input encoding.
177 *
178 * @param fileContents an input stream to analyze
179 * @param encoding input stream's encoding
180 * @param ruleSet set of rules to process against the file
181 * @param ctx context in which PMD is operating. This contains the Report and whatnot
182 * @throws PMDException if the input encoding is unsupported or the input stream could
183 * not be parsed
184 * @see #processFile(Reader, RuleSet, RuleContext)
185 */
186 public void processFile(InputStream fileContents, String encoding,
187 RuleSet ruleSet, RuleContext ctx) throws PMDException {
188 try {
189 processFile(new InputStreamReader(fileContents, encoding), ruleSet, ctx);
190 } catch (UnsupportedEncodingException uee) {
191 throw new PMDException("Unsupported encoding exception: "
192 + uee.getMessage());
193 }
194 }
195
196 /**
197 * Processes the input stream against a rule set using the given input encoding.
198 *
199 * @param fileContents an input stream to analyze
200 * @param encoding input stream's encoding
201 * @param ruleSets set of rules to process against the file
202 * @param ctx context in which PMD is operating. This contains the Report and whatnot
203 * @throws PMDException if the input encoding is unsupported or the input stream could
204 * not be parsed
205 * @see #processFile(Reader, RuleSet, RuleContext)
206 */
207 public void processFile(InputStream fileContents, String encoding,
208 RuleSets ruleSets, RuleContext ctx) throws PMDException {
209 try {
210 processFile(new InputStreamReader(fileContents, encoding), ruleSets, ctx);
211 } catch (UnsupportedEncodingException uee) {
212 throw new PMDException("Unsupported encoding exception: "
213 + uee.getMessage());
214 }
215 }
216
217 /**
218 * Processes the input stream against a rule set assuming the platform character set.
219 *
220 * @param fileContents input stream to check
221 * @param ruleSet the set of rules to process against the source code
222 * @param ctx the context in which PMD is operating. This contains the Report and
223 * whatnot
224 * @throws PMDException if the input encoding is unsupported or the input input stream
225 * could not be parsed
226 * @see #processFile(InputStream, String, RuleSet, RuleContext)
227 */
228 public void processFile(InputStream fileContents, RuleSet ruleSet,
229 RuleContext ctx) throws PMDException {
230 processFile(fileContents, System.getProperty("file.encoding"), ruleSet, ctx);
231 }
232
233 public void setExcludeMarker(String marker) {
234 this.excludeMarker = marker;
235 }
236
237 /**
238 * Set the SourceType to be used for ".java" files.
239 *
240 * @param javaVersion the SourceType that indicates the java version
241 */
242 public void setJavaVersion(SourceType javaVersion) {
243 sourceTypeDiscoverer.setSourceTypeOfJavaFiles(javaVersion);
244 }
245
246 /**
247 * Get the ClassLoader being used by PMD when processing Rules.
248 * @return The ClassLoader being used
249 */
250 public ClassLoader getClassLoader() {
251 return classLoader;
252 }
253
254 /**
255 * Set the ClassLoader being used by PMD when processing Rules.
256 * Setting a value of <code>null</code> will cause the default
257 * ClassLoader to be used.
258 * @param classLoader The ClassLoader to use
259 */
260 public void setClassLoader(ClassLoader classLoader) {
261 if (classLoader == null) {
262 classLoader = getClass().getClassLoader();
263 }
264 this.classLoader = classLoader;
265 }
266
267 /**
268 * Create a ClassLoader which loads classes using a CLASSPATH like String.
269 * If the String looks like a URL to a file (e.g. starts with <code>file://</code>)
270 * the file will be read with each line representing an entry on the classpath.
271 * <p>
272 * The ClassLoader used to load the <code>net.sourceforge.pmd.PMD</code> class
273 * will be used as the parent ClassLoader of the created ClassLoader.
274 *
275 * @param classpath The classpath String.
276 * @return A ClassLoader
277 * @throws IOException
278 * @see ClasspathClassLoader
279 */
280 public static ClassLoader createClasspathClassLoader(String classpath) throws IOException {
281 ClassLoader classLoader = PMD.class.getClassLoader();
282 if (classpath != null) {
283 classLoader = new ClasspathClassLoader(classpath, classLoader);
284 }
285 return classLoader;
286 }
287
288 private static void doPMD(CommandLineOptions opts){
289 long startFiles = System.nanoTime();
290 SourceFileSelector fileSelector = new SourceFileSelector();
291
292 fileSelector.setSelectJavaFiles(opts.isCheckJavaFiles());
293 fileSelector.setSelectJspFiles(opts.isCheckJspFiles());
294
295 List<DataSource> files;
296 if (opts.containsCommaSeparatedFileList()) {
297 files = collectFromCommaDelimitedString(opts.getInputPath(),
298 fileSelector);
299 } else {
300 files = collectFilesFromOneName(opts.getInputPath(), fileSelector);
301 }
302 long endFiles = System.nanoTime();
303 Benchmark.mark(Benchmark.TYPE_COLLECT_FILES, endFiles - startFiles, 0);
304
305 SourceType sourceType;
306 if (opts.getTargetJDK().equals("1.3")) {
307 LOG.fine("In JDK 1.3 mode");
308 sourceType = SourceType.JAVA_13;
309 } else if (opts.getTargetJDK().equals("1.5")) {
310 LOG.fine("In JDK 1.5 mode");
311 sourceType = SourceType.JAVA_15;
312 } else if (opts.getTargetJDK().equals("1.6")) {
313 LOG.fine("In JDK 1.6 mode");
314 sourceType = SourceType.JAVA_16;
315 } else if (opts.getTargetJDK().equals("1.7")) {
316 LOG.fine("In JDK 1.7 mode");
317 sourceType = SourceType.JAVA_17;
318 } else {
319 LOG.fine("In JDK 1.4 mode");
320 sourceType = SourceType.JAVA_14;
321 }
322
323 final ClassLoader classLoader;
324 try {
325 classLoader = createClasspathClassLoader(opts.getAuxClasspath());
326 } catch (IOException e) {
327 LOG.log(Level.SEVERE, "Bad -auxclasspath argument", e);
328 System.out.println(opts.usage());
329 return;
330 }
331
332 long reportStart, reportEnd;
333 Renderer renderer;
334 Writer w = null;
335
336 reportStart = System.nanoTime();
337 try {
338 renderer = opts.createRenderer();
339 List<Renderer> renderers = new LinkedList<Renderer>();
340 renderers.add(renderer);
341 if (opts.getReportFile() != null) {
342 w = new BufferedWriter(new FileWriter(opts.getReportFile()));
343 } else {
344 w = new OutputStreamWriter(System.out);
345 }
346 renderer.setWriter(w);
347 renderer.start();
348
349 reportEnd = System.nanoTime();
350 Benchmark.mark(Benchmark.TYPE_REPORTING, reportEnd - reportStart, 0);
351
352 RuleContext ctx = new RuleContext();
353
354 try {
355 long startLoadRules = System.nanoTime();
356 RuleSetFactory ruleSetFactory = new RuleSetFactory();
357 ruleSetFactory.setMinimumPriority(opts.getMinPriority());
358
359 RuleSets rulesets = ruleSetFactory.createRuleSets(opts.getRulesets());
360 printRuleNamesInDebug(rulesets);
361 long endLoadRules = System.nanoTime();
362 Benchmark.mark(Benchmark.TYPE_LOAD_RULES, endLoadRules - startLoadRules, 0);
363
364 processFiles(opts.getCpus(), ruleSetFactory, sourceType, files, ctx, renderers,
365 opts.stressTestEnabled(),
366 opts.getRulesets(), opts.shortNamesEnabled(),
367 opts.getInputPath(), opts.getEncoding(), opts.getExcludeMarker(), classLoader);
368 } catch (RuleSetNotFoundException rsnfe) {
369 LOG.log(Level.SEVERE, "Ruleset not found", rsnfe);
370 System.out.println(opts.usage());
371 }
372
373 reportStart = System.nanoTime();
374 renderer.end();
375 w.write(EOL);
376 w.flush();
377 if (opts.getReportFile() != null) {
378 w.close();
379 w = null;
380 }
381 } catch (Exception e) {
382 String message = e.getMessage();
383 if (message != null) {
384 LOG.severe(message);
385 } else {
386 LOG.log(Level.SEVERE, "Exception during processing", e);
387 }
388
389 LOG.log(Level.FINE, "Exception during processing", e);
390
391 LOG.info(opts.usage());
392 } finally {
393 if (opts.getReportFile() != null && w != null) {
394 try {
395 w.close();
396 } catch (Exception e) {
397 System.out.println(e.getMessage());
398 }
399 }
400 reportEnd = System.nanoTime();
401 Benchmark.mark(Benchmark.TYPE_REPORTING, reportEnd - reportStart, 0);
402 }
403 }
404
405 public static void main(String[] args) {
406 long start = System.nanoTime();
407 final CommandLineOptions opts = new CommandLineOptions(args);
408
409 final Level logLevel = opts.debugEnabled() ? Level.FINER : Level.INFO;
410 final Handler logHandler = new ConsoleLogHandler();
411 final ScopedLogHandlersManager logHandlerManager = new ScopedLogHandlersManager(logLevel, logHandler);
412 final Level oldLogLevel = LOG.getLevel();
413 LOG.setLevel(logLevel);
414 try{
415 doPMD(opts);
416 }finally{
417 logHandlerManager.close();
418 LOG.setLevel(oldLogLevel);
419 if (opts.benchmark()) {
420 long end = System.nanoTime();
421 Benchmark.mark(Benchmark.TYPE_TOTAL_PMD, end - start, 0);
422 System.err.println(Benchmark.report());
423 }
424 }
425 }
426
427 private static class PmdRunnable extends PMD implements Callable<Report> {
428 private final ExecutorService executor;
429 private final DataSource dataSource;
430 private final String fileName;
431 private final String encoding;
432 private final String rulesets;
433 private final List<Renderer> renderers;
434
435 public PmdRunnable(ExecutorService executor, DataSource dataSource, String fileName, SourceType sourceType,
436 List<Renderer> renderers, String encoding, String rulesets, String excludeMarker, ClassLoader classLoader) {
437 this.executor = executor;
438 this.dataSource = dataSource;
439 this.fileName = fileName;
440 this.encoding = encoding;
441 this.rulesets = rulesets;
442 this.renderers = renderers;
443
444 setJavaVersion(sourceType);
445 setExcludeMarker(excludeMarker);
446 setClassLoader(classLoader);
447 }
448
449 public Report call() {
450 PmdThread thread = (PmdThread) Thread.currentThread();
451
452 RuleContext ctx = thread.getRuleContext();
453 RuleSets rs = thread.getRuleSets(rulesets);
454
455 Report report = new Report();
456 ctx.setReport(report);
457
458 ctx.setSourceCodeFilename(fileName);
459 ctx.setSourceCodeFile(new File(fileName));
460 if (LOG.isLoggable(Level.FINE)) {
461 LOG.fine("Processing " + ctx.getSourceCodeFilename());
462 }
463 for(Renderer r: renderers) {
464 r.startFileAnalysis(dataSource);
465 }
466
467 try {
468 InputStream stream = new BufferedInputStream(dataSource.getInputStream());
469 processFile(stream, encoding, rs, ctx);
470 } catch (PMDException pmde) {
471 LOG.log(Level.FINE, "Error while processing file", pmde.getCause());
472
473 report.addError(
474 new Report.ProcessingError(pmde.getMessage(),
475 fileName));
476 } catch (IOException ioe) {
477
478 LOG.log(Level.FINE, "IOException during processing", ioe);
479
480 report.addError(
481 new Report.ProcessingError(ioe.getMessage(),
482 fileName));
483
484 executor.shutdownNow();
485 } catch (RuntimeException re) {
486
487 LOG.log(Level.FINE, "RuntimeException during processing", re);
488
489 report.addError(
490 new Report.ProcessingError(re.getMessage(),
491 fileName));
492
493 executor.shutdownNow();
494 }
495 return report;
496 }
497
498 }
499
500 private static class PmdThreadFactory implements ThreadFactory {
501
502 private final RuleSetFactory ruleSetFactory;
503 private final RuleContext ctx;
504 private final AtomicInteger counter = new AtomicInteger();
505
506 public PmdThreadFactory(RuleSetFactory ruleSetFactory, RuleContext ctx) {
507 this.ruleSetFactory = ruleSetFactory;
508 this.ctx = ctx;
509 }
510
511 public Thread newThread(Runnable r) {
512 PmdThread t = new PmdThread(counter.incrementAndGet(), r, ruleSetFactory, ctx);
513 threadList.add(t);
514 return t;
515 }
516
517 public List<PmdThread> threadList = Collections.synchronizedList(new LinkedList<PmdThread>());
518
519 }
520
521 private static class PmdThread extends Thread {
522
523 public PmdThread(int id, Runnable r, RuleSetFactory ruleSetFactory, RuleContext ctx) {
524 super(r, "PmdThread " + id);
525 this.id = id;
526 context = new RuleContext(ctx);
527 this.ruleSetFactory = ruleSetFactory;
528 }
529
530 private int id;
531 private RuleContext context;
532 private RuleSets rulesets;
533 private RuleSetFactory ruleSetFactory;
534
535 public RuleContext getRuleContext() {
536 return context;
537 }
538
539 public RuleSets getRuleSets(String rsList) {
540 if (rulesets == null) {
541 try {
542 rulesets = ruleSetFactory.createRuleSets(rsList);
543 } catch (Exception e) {
544 e.printStackTrace();
545 }
546 }
547 return rulesets;
548 }
549
550 public String toString() {
551 return "PmdThread " + id;
552 }
553
554 }
555
556 /**
557 * Do we have proper permissions to use multithreading?
558 */
559 private static final boolean mtSupported;
560
561 static {
562 boolean error = false;
563 try {
564
565
566
567
568
569
570 ExecutorService executor = Executors.newFixedThreadPool(1);
571 executor.shutdown();
572 } catch (RuntimeException e) {
573 error = true;
574 }
575 mtSupported = !error;
576 }
577
578 /**
579 * Run PMD on a list of files using multiple threads.
580 *
581 * @throws IOException If one of the files could not be read
582 */
583 public static void processFiles(int threadCount, RuleSetFactory ruleSetFactory, SourceType sourceType, List<DataSource> files, RuleContext ctx,
584 List<Renderer> renderers, String rulesets, final boolean shortNamesEnabled, final String inputPath,
585 String encoding, String excludeMarker, ClassLoader classLoader) {
586 processFiles(threadCount, ruleSetFactory, sourceType, files, ctx,
587 renderers, false, rulesets, shortNamesEnabled, inputPath,
588 encoding, excludeMarker, classLoader);
589 }
590 /**
591 * Run PMD on a list of files using multiple threads.
592 *
593 * @throws IOException If one of the files could not be read
594 */
595 public static void processFiles(int threadCount, RuleSetFactory ruleSetFactory, SourceType sourceType, List<DataSource> files, RuleContext ctx,
596 List<Renderer> renderers, boolean stressTestEnabled, String rulesets, final boolean shortNamesEnabled, final String inputPath,
597 String encoding, String excludeMarker, ClassLoader classLoader) {
598
599
600
601
602
603
604 boolean useMT = mtSupported && (threadCount > 0);
605
606 if (stressTestEnabled) {
607
608 Collections.shuffle(files);
609 } else {
610 Collections.sort(files, new Comparator<DataSource>() {
611 public int compare(DataSource d1,DataSource d2) {
612 String s1 = d1.getNiceFileName(shortNamesEnabled, inputPath);
613 String s2 = d2.getNiceFileName(shortNamesEnabled, inputPath);
614 return s1.compareTo(s2);
615 }
616 });
617 }
618
619 if (useMT) {
620 RuleSets rs = null;
621 try {
622 rs = ruleSetFactory.createRuleSets(rulesets);
623 } catch (RuleSetNotFoundException rsnfe) {
624
625 }
626 rs.start(ctx);
627
628 PmdThreadFactory factory = new PmdThreadFactory(ruleSetFactory, ctx);
629 ExecutorService executor = Executors.newFixedThreadPool(threadCount, factory);
630 List<Future<Report>> tasks = new LinkedList<Future<Report>>();
631
632 for (DataSource dataSource:files) {
633 String niceFileName = dataSource.getNiceFileName(shortNamesEnabled,
634 inputPath);
635
636 PmdRunnable r = new PmdRunnable(executor, dataSource, niceFileName, sourceType, renderers,
637 encoding, rulesets, excludeMarker, classLoader);
638
639 Future<Report> future = executor.submit(r);
640 tasks.add(future);
641 }
642 executor.shutdown();
643
644 while (!tasks.isEmpty()) {
645 Future<Report> future = tasks.remove(0);
646 Report report = null;
647 try {
648 report = future.get();
649 } catch (InterruptedException ie) {
650 Thread.currentThread().interrupt();
651 future.cancel(true);
652 } catch (ExecutionException ee) {
653 Throwable t = ee.getCause();
654 if (t instanceof RuntimeException) {
655 throw (RuntimeException) t;
656 } else if (t instanceof Error) {
657 throw (Error) t;
658 } else {
659 throw new IllegalStateException("PmdRunnable exception", t);
660 }
661 }
662
663 try {
664 long start = System.nanoTime();
665 for (Renderer r: renderers) {
666 r.renderFileReport(report);
667 }
668 long end = System.nanoTime();
669 Benchmark.mark(Benchmark.TYPE_REPORTING, end - start, 1);
670 } catch (IOException ioe) {
671 }
672 }
673
674 try {
675 rs.end(ctx);
676 long start = System.nanoTime();
677 for (Renderer r: renderers) {
678 r.renderFileReport(ctx.getReport());
679 }
680 long end = System.nanoTime();
681 Benchmark.mark(Benchmark.TYPE_REPORTING, end - start, 1);
682 } catch (IOException ioe) {
683 }
684
685
686 } else {
687
688
689 PMD pmd = new PMD();
690 pmd.setJavaVersion(sourceType);
691 pmd.setExcludeMarker(excludeMarker);
692
693 RuleSets rs = null;
694 try {
695 rs = ruleSetFactory.createRuleSets(rulesets);
696 } catch (RuleSetNotFoundException rsnfe) {
697
698 }
699 for (DataSource dataSource: files) {
700 String niceFileName = dataSource.getNiceFileName(shortNamesEnabled,
701 inputPath);
702
703 Report report = new Report();
704 ctx.setReport(report);
705
706 ctx.setSourceCodeFilename(niceFileName);
707 ctx.setSourceCodeFile(new File(niceFileName));
708 if (LOG.isLoggable(Level.FINE)) {
709 LOG.fine("Processing " + ctx.getSourceCodeFilename());
710 }
711 rs.start(ctx);
712
713 for(Renderer r: renderers) {
714 r.startFileAnalysis(dataSource);
715 }
716
717 try {
718 InputStream stream = new BufferedInputStream(dataSource.getInputStream());
719 pmd.processFile(stream, encoding, rs, ctx);
720 } catch (PMDException pmde) {
721 LOG.log(Level.FINE, "Error while processing file", pmde.getCause());
722
723 report.addError(
724 new Report.ProcessingError(pmde.getMessage(),
725 niceFileName));
726 } catch (IOException ioe) {
727
728 LOG.log(Level.FINE, "Unable to read source file", ioe);
729
730 report.addError(
731 new Report.ProcessingError(ioe.getMessage(),
732 niceFileName));
733 } catch (RuntimeException re) {
734
735 LOG.log(Level.FINE, "RuntimeException while processing file", re);
736
737 report.addError(
738 new Report.ProcessingError(re.getMessage(),
739 niceFileName));
740 }
741
742 rs.end(ctx);
743
744 try {
745 long start = System.nanoTime();
746 for (Renderer r: renderers) {
747 r.renderFileReport(report);
748 }
749 long end = System.nanoTime();
750 Benchmark.mark(Benchmark.TYPE_REPORTING, end - start, 1);
751 } catch (IOException ioe) {
752 }
753 }
754 }
755 }
756
757 /**
758 * Run PMD on a list of files.
759 *
760 * @param files the List of DataSource instances.
761 * @param ctx the context in which PMD is operating. This contains the Report and
762 * whatnot
763 * @param rulesets the RuleSets
764 * @param debugEnabled
765 * @param shortNamesEnabled
766 * @param inputPath
767 * @param encoding
768 * @throws IOException If one of the files could not be read
769 */
770 public void processFiles(List<DataSource> files, RuleContext ctx, RuleSets rulesets,
771 boolean debugEnabled, boolean shortNamesEnabled, String inputPath,
772 String encoding) throws IOException {
773 for (DataSource dataSource: files) {
774 String niceFileName = dataSource.getNiceFileName(shortNamesEnabled,
775 inputPath);
776 ctx.setSourceCodeFilename(niceFileName);
777 ctx.setSourceCodeFile(new File(niceFileName));
778 LOG.fine("Processing " + ctx.getSourceCodeFilename());
779
780 try {
781 InputStream stream = new BufferedInputStream(dataSource
782 .getInputStream());
783 processFile(stream, encoding, rulesets, ctx);
784 } catch (PMDException pmde) {
785 LOG.log(Level.FINE, "Error while processing files", pmde.getCause());
786
787 ctx.getReport().addError(new Report.ProcessingError(pmde.getMessage(), niceFileName));
788 }
789 }
790 }
791
792 /**
793 * If in debug modus, print the names of the rules.
794 *
795 * @param rulesets the RuleSets to print
796 */
797 private static void printRuleNamesInDebug(RuleSets rulesets) {
798 if (LOG.isLoggable(Level.FINER)) {
799 for (Rule r: rulesets.getAllRules()) {
800 LOG.finer("Loaded rule " + r.getName());
801 }
802 }
803 }
804
805 /**
806 * Collects the given file into a list.
807 *
808 * @param inputFileName a file name
809 * @param fileSelector Filtering of wanted source files
810 * @return the list of files collected from the <code>inputFileName</code>
811 * @see #collect(String, SourceFileSelector)
812 */
813 private static List<DataSource> collectFilesFromOneName(String inputFileName,
814 SourceFileSelector fileSelector) {
815 return collect(inputFileName, fileSelector);
816 }
817
818 /**
819 * Collects the files from the given comma-separated list.
820 *
821 * @param fileList comma-separated list of filenames
822 * @param fileSelector Filtering of wanted source files
823 * @return list of files collected from the <code>fileList</code>
824 */
825 private static List<DataSource> collectFromCommaDelimitedString(String fileList,
826 SourceFileSelector fileSelector) {
827 List<DataSource> files = new ArrayList<DataSource>();
828 for (StringTokenizer st = new StringTokenizer(fileList, ","); st
829 .hasMoreTokens();) {
830 files.addAll(collect(st.nextToken(), fileSelector));
831 }
832 return files;
833 }
834
835 /**
836 * Collects the files from the given <code>filename</code>.
837 *
838 * @param filename the source from which to collect files
839 * @param fileSelector Filtering of wanted source files
840 * @return a list of files found at the given <code>filename</code>
841 * @throws RuntimeException if <code>filename</code> is not found
842 */
843 private static List<DataSource> collect(String filename, SourceFileSelector fileSelector) {
844 File inputFile = new File(filename);
845 if (!inputFile.exists()) {
846 throw new RuntimeException("File " + inputFile.getName()
847 + " doesn't exist");
848 }
849 List<DataSource> dataSources = new ArrayList<DataSource>();
850 if (!inputFile.isDirectory()) {
851 if (filename.endsWith(".zip") || filename.endsWith(".jar")) {
852 ZipFile zipFile;
853 try {
854 zipFile = new ZipFile(inputFile);
855 Enumeration e = zipFile.entries();
856 while (e.hasMoreElements()) {
857 ZipEntry zipEntry = (ZipEntry) e.nextElement();
858 if (fileSelector.isWantedFile(zipEntry.getName())) {
859 dataSources.add(new ZipDataSource(zipFile, zipEntry));
860 }
861 }
862 } catch (IOException ze) {
863 throw new RuntimeException("Zip file " + inputFile.getName()
864 + " can't be opened");
865 }
866 } else {
867 dataSources.add(new FileDataSource(inputFile));
868 }
869 } else {
870 FileFinder finder = new FileFinder();
871 List<File> files = finder.findFilesFrom(inputFile.getAbsolutePath(),
872 new SourceFileOrDirectoryFilter(fileSelector), true);
873 for (File f: files) {
874 dataSources.add(new FileDataSource(f));
875 }
876 }
877 return dataSources;
878 }
879
880 }