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 }