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 }