View Javadoc

1   /***
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3   */
4   package net.sourceforge.pmd.ant;
5   
6   import net.sourceforge.pmd.PMD;
7   import net.sourceforge.pmd.PMDException;
8   import net.sourceforge.pmd.Report;
9   import net.sourceforge.pmd.Rule;
10  import net.sourceforge.pmd.RuleContext;
11  import net.sourceforge.pmd.RuleSet;
12  import net.sourceforge.pmd.RuleSetFactory;
13  import net.sourceforge.pmd.RuleSetNotFoundException;
14  import net.sourceforge.pmd.TargetJDK1_3;
15  import net.sourceforge.pmd.renderers.Renderer;
16  import net.sourceforge.pmd.renderers.TextRenderer;
17  import org.apache.tools.ant.AntClassLoader;
18  import org.apache.tools.ant.BuildException;
19  import org.apache.tools.ant.DirectoryScanner;
20  import org.apache.tools.ant.Project;
21  import org.apache.tools.ant.Task;
22  import org.apache.tools.ant.types.FileSet;
23  import org.apache.tools.ant.types.Path;
24  import org.apache.tools.ant.types.Reference;
25  
26  import java.io.File;
27  import java.io.FileInputStream;
28  import java.io.FileNotFoundException;
29  import java.io.IOException;
30  import java.io.Writer;
31  import java.util.ArrayList;
32  import java.util.Iterator;
33  import java.util.List;
34  
35  public class PMDTask extends Task {
36  
37      private Path classpath;
38      private List formatters = new ArrayList();
39      private List filesets = new ArrayList();
40      private boolean shortFilenames;
41      private boolean printToConsole;
42      private String ruleSetFiles;
43      private String encoding = System.getProperty("file.encoding");
44      private boolean failOnError;
45      private boolean failOnRuleViolation;
46      private boolean targetJDK13;
47      private String failuresPropertyName;
48  
49      /***
50       * The end of line string for this machine.
51       */
52      protected String EOL = System.getProperty("line.separator", "\n");
53  
54      public void setShortFilenames(boolean value) {
55          this.shortFilenames = value;
56      }
57  
58      public void setTargetJDK13(boolean value) {
59          this.targetJDK13 = value;
60      }
61  
62      public void setFailOnError(boolean fail) {
63          this.failOnError = fail;
64      }
65  
66      public void setFailOnRuleViolation(boolean fail) {
67          this.failOnRuleViolation = fail;
68      }
69  
70      public void setPrintToConsole(boolean printToConsole) {
71          this.printToConsole = printToConsole;
72      }
73  
74      public void setRuleSetFiles(String ruleSetFiles) {
75          this.ruleSetFiles = ruleSetFiles;
76      }
77  
78      public void setEncoding(String encoding) {
79          this.encoding = encoding;
80      }
81  
82      public void setFailuresPropertyName(String failuresPropertyName) {
83          this.failuresPropertyName = failuresPropertyName;
84      }
85  
86      public void addFileset(FileSet set) {
87          filesets.add(set);
88      }
89  
90      public void addFormatter(Formatter f) {
91          formatters.add(f);
92      }
93  
94      public void setClasspath(Path classpath) {
95          this.classpath = classpath;
96      }
97  
98      public Path getClasspath() {
99          return classpath;
100     }
101 
102     public Path createClasspath() {
103         if (classpath == null) {
104             classpath = new Path(getProject());
105         }
106         return classpath.createPath();
107     }
108 
109     public void setClasspathRef(Reference r) {
110         createLongClasspath().setRefid(r);
111     }
112 
113     public void execute() throws BuildException {
114         validate();
115 
116         RuleSet rules;
117         try {
118             RuleSetFactory ruleSetFactory = new RuleSetFactory();
119             if (classpath == null) {
120                 log("Using the normal ClassLoader", Project.MSG_VERBOSE);
121                 rules = ruleSetFactory.createRuleSet(ruleSetFiles);
122             } else {
123                 log("Using the AntClassLoader", Project.MSG_VERBOSE);
124                 rules = ruleSetFactory.createRuleSet(ruleSetFiles, new AntClassLoader(getProject(), classpath));
125             }
126         } catch (RuleSetNotFoundException e) {
127             throw new BuildException(e.getMessage());
128         }
129 
130         logRulesUsed(rules);
131 
132         PMD pmd;
133         if (targetJDK13) {
134             pmd = new PMD(new TargetJDK1_3());
135         } else {
136             pmd = new PMD();
137         }
138 
139         RuleContext ctx = new RuleContext();
140         ctx.setReport(new Report());
141         for (Iterator i = filesets.iterator(); i.hasNext();) {
142             FileSet fs = (FileSet) i.next();
143             DirectoryScanner ds = fs.getDirectoryScanner(getProject());
144             String[] srcFiles = ds.getIncludedFiles();
145             for (int j = 0; j < srcFiles.length; j++) {
146                 File file = new File(ds.getBasedir() + System.getProperty("file.separator") + srcFiles[j]);
147                 log("Processing file " + file.getAbsoluteFile().toString(), Project.MSG_VERBOSE);
148                 ctx.setSourceCodeFilename(shortFilenames ? srcFiles[j] : file.getAbsolutePath());
149                 try {
150                     pmd.processFile(new FileInputStream(file), encoding, rules, ctx);
151                 } catch (FileNotFoundException fnfe) {
152                     if (failOnError) {
153                         throw new BuildException(fnfe);
154                     }
155                 } catch (PMDException pmde) {
156                     log(pmde.toString(), Project.MSG_VERBOSE);
157                     if (pmde.getReason() != null && pmde.getReason().getMessage() != null) {
158                         log(pmde.getReason().getMessage(), Project.MSG_VERBOSE);
159                     }
160                     if (failOnError) {
161                         throw new BuildException(pmde);
162                     }
163                     ctx.getReport().addError(new Report.ProcessingError(pmde.getMessage(), ctx.getSourceCodeFilename()));
164                 }
165             }
166         }
167 
168         log(ctx.getReport().size() + " problems found", Project.MSG_VERBOSE);
169 
170         if (!ctx.getReport().isEmpty()) {
171             for (Iterator i = formatters.iterator(); i.hasNext();) {
172                 Formatter formatter = (Formatter) i.next();
173                 log("Sending a report to " + formatter, Project.MSG_VERBOSE);
174                 String buffer = formatter.getRenderer().render(ctx.getReport()) + EOL;
175                 try {
176                     Writer writer = formatter.getToFileWriter(getProject().getBaseDir().toString());
177                     writer.write(buffer, 0, buffer.length());
178                     writer.close();
179                 } catch (IOException ioe) {
180                     throw new BuildException(ioe.getMessage());
181                 }
182             }
183 
184             if (failuresPropertyName != null && ctx.getReport().size() > 0) {
185                 getProject().setProperty(failuresPropertyName, String.valueOf(ctx.getReport().size()));
186                 log("Setting property " + failuresPropertyName + " to " + String.valueOf(ctx.getReport().size()), Project.MSG_VERBOSE);
187             }
188 
189             if (printToConsole) {
190                 Renderer r = new TextRenderer();
191                 log(r.render(ctx.getReport()), Project.MSG_INFO);
192             }
193 
194             if (failOnRuleViolation) {
195                 throw new BuildException("Stopping build since PMD found " + ctx.getReport().size() + " rule violations in the code");
196             }
197         }
198     }
199 
200     private void logRulesUsed(RuleSet rules) {
201         log("Using these rulesets: " + ruleSetFiles, Project.MSG_VERBOSE);
202         for (Iterator i = rules.getRules().iterator();i.hasNext();) {
203             Rule rule = (Rule)i.next();
204             log("Using rule " + rule.getName(), Project.MSG_VERBOSE);
205         }
206     }
207 
208     private void validate() throws BuildException {
209         if (formatters.isEmpty() && !printToConsole) {
210             throw new BuildException("No formatter specified; and printToConsole was false");
211         }
212 
213         for (Iterator i = formatters.iterator(); i.hasNext();) {
214             Formatter f = (Formatter) i.next();
215             if (f.isToFileNull()) {
216                 throw new BuildException("Formatter toFile attribute is required");
217             }
218         }
219 
220         if (ruleSetFiles == null) {
221             throw new BuildException("No rulesets specified");
222         }
223     }
224 
225     private Path createLongClasspath() {
226         if (classpath == null) {
227             classpath = new Path(getProject());
228         }
229         return classpath.createPath();
230     }
231 
232 }