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