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