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.SimpleRuleSetNameMapper;
15  import net.sourceforge.pmd.TargetJDK1_3;
16  import net.sourceforge.pmd.TargetJDK1_5;
17  import net.sourceforge.pmd.renderers.Renderer;
18  import net.sourceforge.pmd.renderers.TextRenderer;
19  import org.apache.tools.ant.AntClassLoader;
20  import org.apache.tools.ant.BuildException;
21  import org.apache.tools.ant.DirectoryScanner;
22  import org.apache.tools.ant.Project;
23  import org.apache.tools.ant.Task;
24  import org.apache.tools.ant.types.FileSet;
25  import org.apache.tools.ant.types.Path;
26  import org.apache.tools.ant.types.Reference;
27  
28  import java.io.BufferedInputStream;
29  import java.io.File;
30  import java.io.FileInputStream;
31  import java.io.FileNotFoundException;
32  import java.io.IOException;
33  import java.io.PrintWriter;
34  import java.io.StringWriter;
35  import java.io.Writer;
36  import java.util.ArrayList;
37  import java.util.Collection;
38  import java.util.Iterator;
39  import java.util.List;
40  
41  public class PMDTask extends Task {
42  
43      private Path classpath;
44      private List formatters = new ArrayList();
45      private List filesets = new ArrayList();
46      private boolean shortFilenames;
47      private boolean printToConsole;
48      private String ruleSetFiles;
49      private String encoding = System.getProperty("file.encoding");
50      private boolean failOnError;
51      private boolean failOnRuleViolation;
52      private String targetJDK = "1.4";
53      private String failuresPropertyName;
54      private String excludeMarker;
55      private final Collection nestedRules = new ArrayList();
56  
57      public void setShortFilenames(boolean value) {
58          this.shortFilenames = value;
59      }
60  
61      public void setTargetJDK(String value) {
62          this.targetJDK = value;
63      }
64  
65      public void setExcludeMarker(String value) {
66          this.excludeMarker = value;
67      }
68  
69      public void setFailOnError(boolean fail) {
70          this.failOnError = fail;
71      }
72  
73      public void setFailOnRuleViolation(boolean fail) {
74          this.failOnRuleViolation = fail;
75      }
76  
77      public void setPrintToConsole(boolean printToConsole) {
78          this.printToConsole = printToConsole;
79      }
80  
81      public void setRuleSetFiles(String ruleSetFiles) {
82          this.ruleSetFiles = ruleSetFiles;
83      }
84  
85      public void setEncoding(String encoding) {
86          this.encoding = encoding;
87      }
88  
89      public void setFailuresPropertyName(String failuresPropertyName) {
90          this.failuresPropertyName = failuresPropertyName;
91      }
92  
93      public void addFileset(FileSet set) {
94          filesets.add(set);
95      }
96  
97      public void addFormatter(Formatter f) {
98          formatters.add(f);
99      }
100 
101     public void setClasspath(Path classpath) {
102         this.classpath = classpath;
103     }
104 
105     public Path getClasspath() {
106         return classpath;
107     }
108 
109     public Path createClasspath() {
110         if (classpath == null) {
111             classpath = new Path(getProject());
112         }
113         return classpath.createPath();
114     }
115 
116     public void setClasspathRef(Reference r) {
117         createLongClasspath().setRefid(r);
118     }
119 
120     public void execute() throws BuildException {
121         validate();
122 
123         ruleSetFiles = new SimpleRuleSetNameMapper(ruleSetFiles).getRuleSets();
124         RuleSet rules;
125         try {
126             RuleSetFactory ruleSetFactory = new RuleSetFactory();
127             if (classpath == null) {
128                 log("Using the normal ClassLoader", Project.MSG_VERBOSE);
129                 rules = ruleSetFactory.createRuleSet(ruleSetFiles);
130             } else {
131                 log("Using the AntClassLoader", Project.MSG_VERBOSE);
132                 rules = ruleSetFactory.createRuleSet(ruleSetFiles, new AntClassLoader(getProject(), classpath));
133             }
134         } catch (RuleSetNotFoundException e) {
135             throw new BuildException(e.getMessage());
136         }
137         logRulesUsed(rules);
138 
139         PMD pmd;
140         if (targetJDK.equals("1.3")) {
141             pmd = new PMD(new TargetJDK1_3());
142         } else if (targetJDK.equals("1.5")) {
143             pmd = new PMD(new TargetJDK1_5());
144         } else {
145             pmd = new PMD();
146         }
147         if (excludeMarker != null) {
148             log("Setting exclude marker to be " + excludeMarker, Project.MSG_VERBOSE);
149             pmd.setExcludeMarker(excludeMarker);
150         }
151 
152         RuleContext ctx = new RuleContext();
153         Report report = new Report();
154         ctx.setReport(report);
155         report.start();
156         for (Iterator i = filesets.iterator(); i.hasNext();) {
157             FileSet fs = (FileSet) i.next();
158             DirectoryScanner ds = fs.getDirectoryScanner(getProject());
159             String[] srcFiles = ds.getIncludedFiles();
160             for (int j = 0; j < srcFiles.length; j++) {
161                 File file = new File(ds.getBasedir() + System.getProperty("file.separator") + srcFiles[j]);
162                 log("Processing file " + file.getAbsoluteFile().toString(), Project.MSG_VERBOSE);
163                 ctx.setSourceCodeFilename(shortFilenames ? srcFiles[j] : file.getAbsolutePath());
164                 try {
165                     pmd.processFile(new BufferedInputStream(new FileInputStream(file)), encoding, rules, ctx);
166                 } catch (FileNotFoundException fnfe) {
167                     if (failOnError) {
168                         throw new BuildException(fnfe);
169                     }
170                 } catch (PMDException pmde) {
171                     log(pmde.toString(), Project.MSG_VERBOSE);
172                     if (pmde.getReason() != null) {
173                         StringWriter strWriter = new StringWriter();
174                         PrintWriter printWriter = new PrintWriter(strWriter);
175                         pmde.getReason().printStackTrace(printWriter);
176                         log(strWriter.toString(), Project.MSG_VERBOSE);
177                     }
178                     if (pmde.getReason() != null && pmde.getReason().getMessage() != null) {
179                         log(pmde.getReason().getMessage(), Project.MSG_VERBOSE);
180                     }
181                     if (failOnError) {
182                         throw new BuildException(pmde);
183                     }
184                     ctx.getReport().addError(new Report.ProcessingError(pmde.getMessage(), ctx.getSourceCodeFilename()));
185                 }
186             }
187         }
188         report.end();
189 
190         log(ctx.getReport().size() + " problems found", Project.MSG_VERBOSE);
191 
192         for (Iterator i = formatters.iterator(); i.hasNext();) {
193             Formatter formatter = (Formatter) i.next();
194             log("Sending a report to " + formatter, Project.MSG_VERBOSE);
195             String buffer = formatter.getRenderer().render(ctx.getReport()) + PMD.EOL;
196             try {
197                 Writer writer = formatter.getToFileWriter(getProject().getBaseDir().toString());
198                 writer.write(buffer, 0, buffer.length());
199                 writer.close();
200             } catch (IOException ioe) {
201                 throw new BuildException(ioe.getMessage());
202             }
203         }
204 
205         if (failuresPropertyName != null && ctx.getReport().size() > 0) {
206             getProject().setProperty(failuresPropertyName, String.valueOf(ctx.getReport().size()));
207             log("Setting property " + failuresPropertyName + " to " + String.valueOf(ctx.getReport().size()), Project.MSG_VERBOSE);
208         }
209 
210         if (printToConsole) {
211             Renderer r = new TextRenderer();
212             log(r.render(ctx.getReport()), Project.MSG_INFO);
213         }
214 
215         if (failOnRuleViolation && ctx.getReport().size() > 0) {
216             throw new BuildException("Stopping build since PMD found " + ctx.getReport().size() + " rule violations in the code");
217         }
218     }
219 
220     private void logRulesUsed(net.sourceforge.pmd.RuleSet rules) {
221         log("Using these rulesets: " + ruleSetFiles, Project.MSG_VERBOSE);
222         for (Iterator i = rules.getRules().iterator(); i.hasNext();) {
223             Rule rule = (Rule) i.next();
224             log("Using rule " + rule.getName(), Project.MSG_VERBOSE);
225         }
226     }
227 
228     private void validate() throws BuildException {
229         if (formatters.isEmpty() && !printToConsole) {
230             throw new BuildException("No formatter specified; and printToConsole was false");
231         }
232 
233         for (Iterator i = formatters.iterator(); i.hasNext();) {
234             Formatter f = (Formatter) i.next();
235             if (f.isToFileNull()) {
236                 throw new BuildException("Formatter toFile attribute is required");
237             }
238         }
239 
240         if (ruleSetFiles == null) {
241             if (nestedRules.isEmpty()) {
242                 throw new BuildException("No rulesets specified");
243             }
244             ruleSetFiles = getNestedRuleSetFiles();            
245         }
246 
247         if (!targetJDK.equals("1.3") && !targetJDK.equals("1.4") && !targetJDK.equals("1.5")) {
248             throw new BuildException("The targetjdk attribute, if used, must be set to either '1.3', '1.4', or '1.5'");
249         }
250     }
251 
252     private String getNestedRuleSetFiles() {
253         final StringBuffer sb = new StringBuffer();
254         for (Iterator it = nestedRules.iterator() ; it.hasNext() ; ) {
255             RuleSetWrapper rs = (RuleSetWrapper) it.next();
256             sb.append(rs.getFile());
257             if (it.hasNext()) {
258                 sb.append(',');
259             }
260         }
261         return sb.toString();
262     }
263 
264     private Path createLongClasspath() {
265         if (classpath == null) {
266             classpath = new Path(getProject());
267         }
268         return classpath.createPath();
269     }
270 
271     public void addRuleset(RuleSetWrapper r) {
272         nestedRules.add(r);
273     }
274     
275 }