KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > pmd > PMD


1 /**
2  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3  */

4 package net.sourceforge.pmd;
5
6 import net.sourceforge.pmd.ast.ParseException;
7 import net.sourceforge.pmd.cpd.FileFinder;
8 import net.sourceforge.pmd.cpd.SourceFileOrDirectoryFilter;
9 import net.sourceforge.pmd.parsers.Parser;
10 import net.sourceforge.pmd.renderers.Renderer;
11 import net.sourceforge.pmd.sourcetypehandlers.SourceTypeHandler;
12 import net.sourceforge.pmd.sourcetypehandlers.SourceTypeHandlerBroker;
13
14 import java.io.BufferedInputStream JavaDoc;
15 import java.io.BufferedWriter JavaDoc;
16 import java.io.File JavaDoc;
17 import java.io.FileWriter JavaDoc;
18 import java.io.IOException JavaDoc;
19 import java.io.InputStream JavaDoc;
20 import java.io.InputStreamReader JavaDoc;
21 import java.io.OutputStreamWriter JavaDoc;
22 import java.io.Reader JavaDoc;
23 import java.io.UnsupportedEncodingException JavaDoc;
24 import java.io.Writer JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Enumeration JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.LinkedList JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.StringTokenizer JavaDoc;
31 import edu.emory.mathcs.backport.java.util.concurrent.ExecutorService;
32 import edu.emory.mathcs.backport.java.util.concurrent.Executors;
33 import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory;
34 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
35 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger;
36 import edu.emory.mathcs.backport.java.util.Collections;
37 import java.util.zip.ZipEntry JavaDoc;
38 import java.util.zip.ZipFile JavaDoc;
39
40 public class PMD {
41     public static final String JavaDoc EOL = System.getProperty("line.separator", "\n");
42     public static final String JavaDoc VERSION = "3.9";
43     public static final String JavaDoc EXCLUDE_MARKER = "NOPMD";
44
45
46     private String JavaDoc excludeMarker = EXCLUDE_MARKER;
47     private SourceTypeDiscoverer sourceTypeDiscoverer = new SourceTypeDiscoverer();
48
49     public PMD() {}
50
51     /**
52      * Processes the file read by the reader agains the rule set.
53      *
54      * @param reader input stream reader
55      * @param ruleSets set of rules to process against the file
56      * @param ctx context in which PMD is operating. This contains the Renderer and
57      * whatnot
58      * @throws PMDException if the input could not be parsed or processed
59      */

60     public void processFile(Reader JavaDoc reader, RuleSets ruleSets, RuleContext ctx)
61             throws PMDException {
62         SourceType sourceType = getSourceTypeOfFile(ctx.getSourceCodeFilename());
63
64         processFile(reader, ruleSets, ctx, sourceType);
65     }
66
67     /**
68      * Processes the file read by the reader against the rule set.
69      *
70      * @param reader input stream reader
71      * @param ruleSets set of rules to process against the file
72      * @param ctx context in which PMD is operating. This contains the Renderer and
73      * whatnot
74      * @param sourceType the SourceType of the source
75      * @throws PMDException if the input could not be parsed or processed
76      */

77     public void processFile(Reader JavaDoc reader, RuleSets ruleSets, RuleContext ctx,
78                             SourceType sourceType) throws PMDException {
79         try {
80             SourceTypeHandler sourceTypeHandler = SourceTypeHandlerBroker.getVisitorsFactoryForSourceType(sourceType);
81             ctx.setSourceType(sourceType);
82             Parser parser = sourceTypeHandler.getParser();
83             parser.setExcludeMarker(excludeMarker);
84             Object JavaDoc rootNode = parser.parse(reader);
85             ctx.excludeLines(parser.getExcludeMap());
86             Thread.yield();
87             sourceTypeHandler.getSymbolFacade().start(rootNode);
88
89             Language language = SourceTypeToRuleLanguageMapper.getMappedLanguage(sourceType);
90
91             if (ruleSets.usesDFA(language)) {
92                 sourceTypeHandler.getDataFlowFacade().start(rootNode);
93             }
94
95             if (ruleSets.usesTypeResolution(language)) {
96                 sourceTypeHandler.getTypeResolutionFacade().start(rootNode);
97             }
98
99             List JavaDoc acus = new ArrayList JavaDoc();
100             acus.add(rootNode);
101
102             ruleSets.apply(acus, ctx, language);
103         } catch (ParseException pe) {
104             throw new PMDException("Error while parsing "
105                     + ctx.getSourceCodeFilename(), pe);
106         } catch (Exception JavaDoc e) {
107             throw new PMDException("Error while processing "
108                     + ctx.getSourceCodeFilename(), e);
109         } finally {
110             try {
111                 reader.close();
112             } catch (IOException JavaDoc e) {
113                 throw new PMDException("Error while closing "
114                         + ctx.getSourceCodeFilename(), e);
115             }
116         }
117     }
118
119     /**
120      * Get the SourceType of the source file with given name. This depends on the fileName
121      * extension, and the java version.
122      * <p/>
123      * For compatibility with older code that does not always pass in a correct filename,
124      * unrecognized files are assumed to be java files.
125      *
126      * @param fileName Name of the file, can be absolute, or simple.
127      * @return the SourceType
128      */

129     private SourceType getSourceTypeOfFile(String JavaDoc fileName) {
130         SourceType sourceType = sourceTypeDiscoverer.getSourceTypeOfFile(fileName);
131         if (sourceType == null) {
132             // For compatibility with older code that does not always pass in
133
// a correct filename.
134
sourceType = sourceTypeDiscoverer.getSourceTypeOfJavaFiles();
135         }
136         return sourceType;
137     }
138
139     /**
140      * Processes the file read by the reader agains the rule set.
141      *
142      * @param reader input stream reader
143      * @param ruleSet set of rules to process against the file
144      * @param ctx context in which PMD is operating. This contains the Renderer and
145      * whatnot
146      * @throws PMDException if the input could not be parsed or processed
147      */

148     public void processFile(Reader JavaDoc reader, RuleSet ruleSet, RuleContext ctx)
149             throws PMDException {
150         processFile(reader, new RuleSets(ruleSet), ctx);
151     }
152
153     /**
154      * Processes the input stream agains a rule set using the given input encoding.
155      *
156      * @param fileContents an input stream to analyze
157      * @param encoding input stream's encoding
158      * @param ruleSet set of rules to process against the file
159      * @param ctx context in which PMD is operating. This contains the Report and whatnot
160      * @throws PMDException if the input encoding is unsupported or the input stream could
161      * not be parsed
162      * @see #processFile(Reader, RuleSet, RuleContext)
163      */

164     public void processFile(InputStream JavaDoc fileContents, String JavaDoc encoding,
165                             RuleSet ruleSet, RuleContext ctx) throws PMDException {
166         try {
167             processFile(new InputStreamReader JavaDoc(fileContents, encoding), ruleSet, ctx);
168         } catch (UnsupportedEncodingException JavaDoc uee) {
169             throw new PMDException("Unsupported encoding exception: "
170                     + uee.getMessage());
171         }
172     }
173
174     /**
175      * Processes the input stream agains a rule set using the given input encoding.
176      *
177      * @param fileContents an input stream to analyze
178      * @param encoding input stream's encoding
179      * @param ruleSets set of rules to process against the file
180      * @param ctx context in which PMD is operating. This contains the Report and whatnot
181      * @throws PMDException if the input encoding is unsupported or the input stream could
182      * not be parsed
183      * @see #processFile(Reader, RuleSet, RuleContext)
184      */

185     public void processFile(InputStream JavaDoc fileContents, String JavaDoc encoding,
186                             RuleSets ruleSets, RuleContext ctx) throws PMDException {
187         try {
188             processFile(new InputStreamReader JavaDoc(fileContents, encoding), ruleSets, ctx);
189         } catch (UnsupportedEncodingException JavaDoc uee) {
190             throw new PMDException("Unsupported encoding exception: "
191                     + uee.getMessage());
192         }
193     }
194
195     /**
196      * Processes the input stream against a rule set assuming the platform character set.
197      *
198      * @param fileContents input stream to check
199      * @param ruleSet the set of rules to process against the source code
200      * @param ctx the context in which PMD is operating. This contains the Report and
201      * whatnot
202      * @throws PMDException if the input encoding is unsupported or the input input stream
203      * could not be parsed
204      * @see #processFile(InputStream, String, RuleSet, RuleContext)
205      */

206     public void processFile(InputStream JavaDoc fileContents, RuleSet ruleSet,
207                             RuleContext ctx) throws PMDException {
208         processFile(fileContents, System.getProperty("file.encoding"), ruleSet, ctx);
209     }
210
211     public void setExcludeMarker(String JavaDoc marker) {
212         this.excludeMarker = marker;
213     }
214
215     /**
216      * Set the SourceType to be used for ".java" files.
217      *
218      * @param javaVersion the SourceType that indicates the java version
219      */

220     public void setJavaVersion(SourceType javaVersion) {
221         sourceTypeDiscoverer.setSourceTypeOfJavaFiles(javaVersion);
222     }
223
224     public static void main(String JavaDoc[] args) {
225         CommandLineOptions opts = new CommandLineOptions(args);
226
227         SourceFileSelector fileSelector = new SourceFileSelector();
228
229         fileSelector.setSelectJavaFiles(opts.isCheckJavaFiles());
230         fileSelector.setSelectJspFiles(opts.isCheckJspFiles());
231
232         List JavaDoc files;
233         if (opts.containsCommaSeparatedFileList()) {
234             files = collectFromCommaDelimitedString(opts.getInputPath(),
235                     fileSelector);
236         } else {
237             files = collectFilesFromOneName(opts.getInputPath(), fileSelector);
238         }
239
240         SourceType sourceType;
241         if (opts.getTargetJDK().equals("1.3")) {
242             if (opts.debugEnabled())
243                 System.out.println("In JDK 1.3 mode");
244             sourceType = SourceType.JAVA_13;
245         } else if (opts.getTargetJDK().equals("1.5")) {
246             if (opts.debugEnabled())
247                 System.out.println("In JDK 1.5 mode");
248             sourceType = SourceType.JAVA_15;
249         } else if (opts.getTargetJDK().equals("1.6")) {
250             if (opts.debugEnabled())
251                 System.out.println("In JDK 1.6 mode");
252             sourceType = SourceType.JAVA_16;
253         } else {
254             if (opts.debugEnabled())
255                 System.out.println("In JDK 1.4 mode");
256             sourceType = SourceType.JAVA_14;
257         }
258
259         RuleContext ctx = new RuleContext();
260         Report report = new Report();
261         ctx.setReport(report);
262         report.start();
263
264         try {
265             RuleSetFactory ruleSetFactory = new RuleSetFactory();
266             ruleSetFactory.setMinimumPriority(opts.getMinPriority());
267
268             RuleSets rulesets = ruleSetFactory.createRuleSets(opts.getRulesets());
269             printRuleNamesInDebug(opts.debugEnabled(), rulesets);
270
271             processFiles(opts.getCpus(), ruleSetFactory, sourceType, files, ctx,
272                     opts.getRulesets(), opts.debugEnabled(), opts.shortNamesEnabled(),
273                     opts.getInputPath(), opts.getEncoding(), opts.getExcludeMarker());
274         } catch (RuleSetNotFoundException rsnfe) {
275             System.out.println(opts.usage());
276             rsnfe.printStackTrace();
277         }
278         report.end();
279
280         Writer JavaDoc w = null;
281         try {
282             Renderer r = opts.createRenderer();
283             if (opts.getReportFile() != null) {
284                 w = new BufferedWriter JavaDoc(new FileWriter JavaDoc(opts.getReportFile()));
285             } else {
286                 w = new OutputStreamWriter JavaDoc(System.out);
287             }
288             r.render(w, ctx.getReport());
289             w.write(EOL);
290             w.flush();
291             if (opts.getReportFile() != null) {
292                 w.close();
293             }
294         } catch (Exception JavaDoc e) {
295             System.out.println(e.getMessage());
296             System.out.println(opts.usage());
297             if (opts.debugEnabled()) {
298                 e.printStackTrace();
299             }
300         } finally {
301             if (opts.getReportFile() != null && w != null) {
302                 try {
303                     w.close();
304                 } catch (Exception JavaDoc e) {
305                     System.out.println(e.getMessage());
306                 }
307             }
308         }
309     }
310
311     private static class PmdRunnable extends PMD implements Runnable JavaDoc {
312         private final ExecutorService executor;
313         private final DataSource dataSource;
314         private final String JavaDoc fileName;
315         private final boolean debugEnabled;
316         private final String JavaDoc encoding;
317         private final String JavaDoc rulesets;
318
319         public PmdRunnable(ExecutorService executor, DataSource dataSource, String JavaDoc fileName, SourceType sourceType,
320                 boolean debugEnabled, String JavaDoc encoding, String JavaDoc rulesets, String JavaDoc excludeMarker) {
321             this.executor = executor;
322             this.dataSource = dataSource;
323             this.fileName = fileName;
324             this.debugEnabled = debugEnabled;
325             this.encoding = encoding;
326             this.rulesets = rulesets;
327             
328             setJavaVersion(sourceType);
329             setExcludeMarker(excludeMarker);
330         }
331
332         public void run() {
333             PmdThread thread = (PmdThread) Thread.currentThread();
334
335             RuleContext ctx = thread.getRuleContext();
336             RuleSets rs = thread.getRuleSets(rulesets);
337
338             ctx.setSourceCodeFilename(fileName);
339             if (debugEnabled) {
340                 System.out.println("Processing " + ctx.getSourceCodeFilename());
341             }
342
343             try {
344                 InputStream JavaDoc stream = new BufferedInputStream JavaDoc(dataSource.getInputStream());
345                 processFile(stream, encoding, rs, ctx);
346             } catch (PMDException pmde) {
347                 if (debugEnabled) {
348                     pmde.getReason().printStackTrace();
349                 }
350                 ctx.getReport().addError(
351                         new Report.ProcessingError(pmde.getMessage(),
352                         fileName));
353             } catch (Throwable JavaDoc t) {
354                 // unexepected exception: log and stop executor service
355
if (debugEnabled) {
356                     t.printStackTrace();
357                 }
358                 ctx.getReport().addError(
359                         new Report.ProcessingError(t.getMessage(),
360                         fileName));
361
362                 executor.shutdownNow();
363             }
364         }
365
366     }
367
368     private static class PmdThreadFactory implements ThreadFactory {
369
370         public PmdThreadFactory(RuleSetFactory ruleSetFactory) {
371             this.ruleSetFactory = ruleSetFactory;
372         }
373
374         private final RuleSetFactory ruleSetFactory;
375         private final AtomicInteger counter = new AtomicInteger();
376
377         public Thread JavaDoc newThread(Runnable JavaDoc r) {
378             PmdThread t = new PmdThread(counter.incrementAndGet(), r, ruleSetFactory);
379             threadList.add(t);
380             return t;
381         }
382
383         public List JavaDoc threadList = Collections.synchronizedList(new LinkedList JavaDoc());
384
385     }
386
387     private static class PmdThread extends Thread JavaDoc {
388
389         public PmdThread(int id, Runnable JavaDoc r, RuleSetFactory ruleSetFactory) {
390             super(r, "PmdThread " + id);
391             this.id = id;
392             context = new RuleContext();
393             context.setReport(new Report());
394             this.ruleSetFactory = ruleSetFactory;
395         }
396         
397         private int id;
398         private RuleContext context;
399         private RuleSets rulesets;
400         private RuleSetFactory ruleSetFactory;
401         
402         public RuleContext getRuleContext() {
403             return context;
404         }
405
406         public RuleSets getRuleSets(String JavaDoc rsList) {
407             if (rulesets == null) {
408                 try {
409                     rulesets = ruleSetFactory.createRuleSets(rsList);
410                 } catch (Exception JavaDoc e) {
411                     e.printStackTrace();
412                 }
413             }
414             return rulesets;
415         }
416
417         public String JavaDoc toString() {
418             return "PmdThread " + id;
419         }
420
421     }
422
423     /**
424      * Run PMD on a list of files using multiple threads.
425      *
426      * @throws IOException If one of the files could not be read
427      */

428     public static void processFiles(int threadCount, RuleSetFactory ruleSetFactory, SourceType sourceType, List JavaDoc files, RuleContext ctx, String JavaDoc rulesets,
429             boolean debugEnabled, boolean shortNamesEnabled, String JavaDoc inputPath,
430             String JavaDoc encoding, String JavaDoc excludeMarker) {
431
432         PmdThreadFactory factory = new PmdThreadFactory(ruleSetFactory);
433         ExecutorService executor = Executors.newFixedThreadPool(threadCount, factory);
434
435         for (Iterator JavaDoc i = files.iterator(); i.hasNext();) {
436             DataSource dataSource = (DataSource) i.next();
437             String JavaDoc niceFileName = dataSource.getNiceFileName(shortNamesEnabled,
438                     inputPath);
439
440             Runnable JavaDoc r = new PmdRunnable(executor, dataSource, niceFileName, sourceType, debugEnabled,
441                                             encoding, rulesets, excludeMarker);
442
443             executor.execute(r);
444         }
445         executor.shutdown();
446
447         try {
448             executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
449         } catch (InterruptedException JavaDoc e) {
450         }
451
452         Report mainReport = ctx.getReport();
453         Iterator JavaDoc i = factory.threadList.iterator();
454         while (i.hasNext()) {
455             PmdThread thread = (PmdThread) i.next();
456             Report r = thread.context.getReport();
457             mainReport.merge(r);
458         }
459     }
460
461     /**
462      * Run PMD on a list of files.
463      *
464      * @param files the List of DataSource instances.
465      * @param ctx the context in which PMD is operating. This contains the Report and
466      * whatnot
467      * @param rulesets the RuleSets
468      * @param debugEnabled
469      * @param shortNamesEnabled
470      * @param inputPath
471      * @param encoding
472      * @throws IOException If one of the files could not be read
473      */

474     public void processFiles(List JavaDoc files, RuleContext ctx, RuleSets rulesets,
475                              boolean debugEnabled, boolean shortNamesEnabled, String JavaDoc inputPath,
476                              String JavaDoc encoding) throws IOException JavaDoc {
477         for (Iterator JavaDoc i = files.iterator(); i.hasNext();) {
478             DataSource dataSource = (DataSource) i.next();
479
480             String JavaDoc niceFileName = dataSource.getNiceFileName(shortNamesEnabled,
481                     inputPath);
482             ctx.setSourceCodeFilename(niceFileName);
483             if (debugEnabled) {
484                 System.out.println("Processing " + ctx.getSourceCodeFilename());
485             }
486
487             try {
488                 InputStream JavaDoc stream = new BufferedInputStream JavaDoc(dataSource
489                         .getInputStream());
490                 processFile(stream, encoding, rulesets, ctx);
491             } catch (PMDException pmde) {
492                 if (debugEnabled) {
493                     pmde.getReason().printStackTrace();
494                 }
495                 ctx.getReport().addError(new Report.ProcessingError(pmde.getMessage(), niceFileName));
496             }
497         }
498     }
499
500     /**
501      * If in debug modus, print the names of the rules.
502      *
503      * @param debugEnabled the boolean indicating if debug is enabled
504      * @param rulesets the RuleSets to print
505      */

506     private static void printRuleNamesInDebug(boolean debugEnabled, RuleSets rulesets) {
507         if (debugEnabled) {
508             for (Iterator JavaDoc i = rulesets.getAllRules().iterator(); i.hasNext();) {
509                 Rule r = (Rule) i.next();
510                 System.out.println("Loaded rule " + r.getName());
511             }
512         }
513     }
514
515     /**
516      * Collects the given file into a list.
517      *
518      * @param inputFileName a file name
519      * @param fileSelector Filtering of wanted source files
520      * @return the list of files collected from the <code>inputFileName</code>
521      * @see #collect(String, SourceFileSelector)
522      */

523     private static List JavaDoc collectFilesFromOneName(String JavaDoc inputFileName,
524                                                 SourceFileSelector fileSelector) {
525         return collect(inputFileName, fileSelector);
526     }
527
528     /**
529      * Collects the files from the given comma-separated list.
530      *
531      * @param fileList comma-separated list of filenames
532      * @param fileSelector Filtering of wanted source files
533      * @return list of files collected from the <code>fileList</code>
534      */

535     private static List JavaDoc collectFromCommaDelimitedString(String JavaDoc fileList,
536                                                         SourceFileSelector fileSelector) {
537         List JavaDoc files = new ArrayList JavaDoc();
538         for (StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(fileList, ","); st
539                 .hasMoreTokens();) {
540             files.addAll(collect(st.nextToken(), fileSelector));
541         }
542         return files;
543     }
544
545     /**
546      * Collects the files from the given <code>filename</code>.
547      *
548      * @param filename the source from which to collect files
549      * @param fileSelector Filtering of wanted source files
550      * @return a list of files found at the given <code>filename</code>
551      * @throws RuntimeException if <code>filename</code> is not found
552      */

553     private static List JavaDoc collect(String JavaDoc filename, SourceFileSelector fileSelector) {
554         File JavaDoc inputFile = new File JavaDoc(filename);
555         if (!inputFile.exists()) {
556             throw new RuntimeException JavaDoc("File " + inputFile.getName()
557                     + " doesn't exist");
558         }
559         List JavaDoc dataSources = new ArrayList JavaDoc();
560         if (!inputFile.isDirectory()) {
561             if (filename.endsWith(".zip") || filename.endsWith(".jar")) {
562                 ZipFile JavaDoc zipFile;
563                 try {
564                     zipFile = new ZipFile JavaDoc(inputFile);
565                     Enumeration JavaDoc e = zipFile.entries();
566                     while (e.hasMoreElements()) {
567                         ZipEntry JavaDoc zipEntry = (ZipEntry JavaDoc) e.nextElement();
568                         if (fileSelector.isWantedFile(zipEntry.getName())) {
569                             dataSources.add(new ZipDataSource(zipFile, zipEntry));
570                         }
571                     }
572                 } catch (IOException JavaDoc ze) {
573                     throw new RuntimeException JavaDoc("Zip file " + inputFile.getName()
574                             + " can't be opened");
575                 }
576             } else {
577                 dataSources.add(new FileDataSource(inputFile));
578             }
579         } else {
580             FileFinder finder = new FileFinder();
581             List JavaDoc files = finder.findFilesFrom(inputFile.getAbsolutePath(),
582                     new SourceFileOrDirectoryFilter(fileSelector), true);
583             for (Iterator JavaDoc i = files.iterator(); i.hasNext();) {
584                 dataSources.add(new FileDataSource((File JavaDoc) i.next()));
585             }
586         }
587         return dataSources;
588     }
589
590 }
591
592          
593
Popular Tags