1
2
3
4 package net.sourceforge.pmd.ant.internal;
5
6 import java.io.File;
7 import java.io.IOException;
8 import java.io.PrintWriter;
9 import java.io.StringWriter;
10 import java.util.ArrayList;
11 import java.util.LinkedList;
12 import java.util.List;
13 import java.util.concurrent.atomic.AtomicInteger;
14 import java.util.logging.Handler;
15 import java.util.logging.Level;
16
17 import net.sourceforge.pmd.PMD;
18 import net.sourceforge.pmd.PMDConfiguration;
19 import net.sourceforge.pmd.Report;
20 import net.sourceforge.pmd.Rule;
21 import net.sourceforge.pmd.RuleContext;
22 import net.sourceforge.pmd.RulePriority;
23 import net.sourceforge.pmd.RuleSet;
24 import net.sourceforge.pmd.RuleSetFactory;
25 import net.sourceforge.pmd.RuleSetNotFoundException;
26 import net.sourceforge.pmd.RuleSets;
27 import net.sourceforge.pmd.ant.Formatter;
28 import net.sourceforge.pmd.ant.PMDTask;
29 import net.sourceforge.pmd.ant.SourceLanguage;
30 import net.sourceforge.pmd.lang.LanguageRegistry;
31 import net.sourceforge.pmd.lang.LanguageVersion;
32 import net.sourceforge.pmd.renderers.AbstractRenderer;
33 import net.sourceforge.pmd.renderers.Renderer;
34 import net.sourceforge.pmd.util.StringUtil;
35 import net.sourceforge.pmd.util.datasource.DataSource;
36 import net.sourceforge.pmd.util.datasource.FileDataSource;
37 import net.sourceforge.pmd.util.log.AntLogHandler;
38 import net.sourceforge.pmd.util.log.ScopedLogHandlersManager;
39
40 import org.apache.commons.io.IOUtils;
41 import org.apache.tools.ant.AntClassLoader;
42 import org.apache.tools.ant.BuildException;
43 import org.apache.tools.ant.DirectoryScanner;
44 import org.apache.tools.ant.Project;
45 import org.apache.tools.ant.types.FileSet;
46 import org.apache.tools.ant.types.Path;
47
48 public class PMDTaskImpl {
49
50 private Path classpath;
51 private Path auxClasspath;
52 private final List<Formatter> formatters = new ArrayList<Formatter>();
53 private final List<FileSet> filesets = new ArrayList<FileSet>();
54 private final PMDConfiguration configuration = new PMDConfiguration();
55 private boolean failOnError;
56 private boolean failOnRuleViolation;
57 private int maxRuleViolations = 0;
58 private String failuresPropertyName;
59 private Project project;
60
61 public PMDTaskImpl(PMDTask task) {
62 configuration.setReportShortNames(task.isShortFilenames());
63 configuration.setSuppressMarker(task.getSuppressMarker());
64 this.failOnError = task.isFailOnError();
65 this.failOnRuleViolation = task.isFailOnRuleViolation();
66 this.maxRuleViolations = task.getMaxRuleViolations();
67 if (this.maxRuleViolations > 0) {
68 this.failOnRuleViolation = true;
69 }
70 configuration.setRuleSets(task.getRulesetFiles());
71 if (task.getEncoding() != null) {
72 configuration.setSourceEncoding(task.getEncoding());
73 }
74 configuration.setThreads(task.getThreads());
75 this.failuresPropertyName = task.getFailuresPropertyName();
76 configuration.setMinimumPriority(RulePriority.valueOf(task.getMinimumPriority()));
77
78 SourceLanguage version = task.getSourceLanguage();
79 if (version != null) {
80 LanguageVersion languageVersion = LanguageRegistry.findLanguageVersionByTerseName(version.getName() + " "
81 + version.getVersion());
82 if (languageVersion == null) {
83 throw new BuildException("The following language is not supported:" + version + ".");
84 }
85 configuration.setDefaultLanguageVersion(languageVersion);
86 }
87
88 classpath = task.getClasspath();
89 auxClasspath = task.getAuxClasspath();
90
91 filesets.addAll(task.getFilesets());
92 formatters.addAll(task.getFormatters());
93
94 project = task.getProject();
95 }
96
97 private void doTask() {
98 setupClassLoader();
99
100
101 RuleSetFactory ruleSetFactory = new RuleSetFactory();
102 ruleSetFactory.setClassLoader(configuration.getClassLoader());
103 try {
104
105
106 ruleSetFactory.setMinimumPriority(configuration.getMinimumPriority());
107 ruleSetFactory.setWarnDeprecated(true);
108 String ruleSets = configuration.getRuleSets();
109 if (StringUtil.isNotEmpty(ruleSets)) {
110
111 configuration.setRuleSets(project.replaceProperties(ruleSets));
112 }
113 RuleSets rules = ruleSetFactory.createRuleSets(configuration.getRuleSets());
114 ruleSetFactory.setWarnDeprecated(false);
115 logRulesUsed(rules);
116 } catch (RuleSetNotFoundException e) {
117 throw new BuildException(e.getMessage(), e);
118 }
119
120 if (configuration.getSuppressMarker() != null) {
121 project.log("Setting suppress marker to be " + configuration.getSuppressMarker(), Project.MSG_VERBOSE);
122 }
123
124
125 for (Formatter formatter : formatters) {
126 project.log("Sending a report to " + formatter, Project.MSG_VERBOSE);
127 formatter.start(project.getBaseDir().toString());
128 }
129
130
131
132
133
134
135 RuleContext ctx = new RuleContext();
136 Report errorReport = new Report();
137 final AtomicInteger reportSize = new AtomicInteger();
138 final String separator = System.getProperty("file.separator");
139
140 for (FileSet fs : filesets) {
141 List<DataSource> files = new LinkedList<DataSource>();
142 DirectoryScanner ds = fs.getDirectoryScanner(project);
143 String[] srcFiles = ds.getIncludedFiles();
144 for (String srcFile : srcFiles) {
145 File file = new File(ds.getBasedir() + separator + srcFile);
146 files.add(new FileDataSource(file));
147 }
148
149 final String inputPaths = ds.getBasedir().getPath();
150 configuration.setInputPaths(inputPaths);
151
152 Renderer logRenderer = new AbstractRenderer("log", "Logging renderer") {
153 public void start() {
154
155 }
156
157 public void startFileAnalysis(DataSource dataSource) {
158 project.log("Processing file " + dataSource.getNiceFileName(false, inputPaths), Project.MSG_VERBOSE);
159 }
160
161 public void renderFileReport(Report r) {
162 int size = r.size();
163 if (size > 0) {
164 reportSize.addAndGet(size);
165 }
166 }
167
168 public void end() {
169
170 }
171
172 public String defaultFileExtension() {
173 return null;
174 }
175 };
176 List<Renderer> renderers = new LinkedList<Renderer>();
177 renderers.add(logRenderer);
178 for (Formatter formatter : formatters) {
179 renderers.add(formatter.getRenderer());
180 }
181 try {
182 PMD.processFiles(configuration, ruleSetFactory, files, ctx, renderers);
183 } catch (RuntimeException pmde) {
184 handleError(ctx, errorReport, pmde);
185 }
186 }
187
188 int problemCount = reportSize.get();
189 project.log(problemCount + " problems found", Project.MSG_VERBOSE);
190
191 for (Formatter formatter : formatters) {
192 formatter.end(errorReport);
193 }
194
195 if (failuresPropertyName != null && problemCount > 0) {
196 project.setProperty(failuresPropertyName, String.valueOf(problemCount));
197 project.log("Setting property " + failuresPropertyName + " to " + problemCount, Project.MSG_VERBOSE);
198 }
199
200 if (failOnRuleViolation && problemCount > maxRuleViolations) {
201 throw new BuildException("Stopping build since PMD found " + problemCount + " rule violations in the code");
202 }
203 }
204
205 private void handleError(RuleContext ctx, Report errorReport, RuntimeException pmde) {
206
207 pmde.printStackTrace();
208 project.log(pmde.toString(), Project.MSG_VERBOSE);
209
210 Throwable cause = pmde.getCause();
211
212 if (cause != null) {
213 StringWriter strWriter = new StringWriter();
214 PrintWriter printWriter = new PrintWriter(strWriter);
215 cause.printStackTrace(printWriter);
216 project.log(strWriter.toString(), Project.MSG_VERBOSE);
217 IOUtils.closeQuietly(printWriter);
218
219 if (StringUtil.isNotEmpty(cause.getMessage())) {
220 project.log(cause.getMessage(), Project.MSG_VERBOSE);
221 }
222 }
223
224 if (failOnError) {
225 throw new BuildException(pmde);
226 }
227 errorReport.addError(new Report.ProcessingError(pmde.getMessage(), ctx.getSourceCodeFilename()));
228 }
229
230 private void setupClassLoader() {
231
232 if (classpath == null) {
233 project.log("Using the normal ClassLoader", Project.MSG_VERBOSE);
234 } else {
235 project.log("Using the AntClassLoader", Project.MSG_VERBOSE);
236
237
238
239 boolean parentFirst = true;
240 configuration.setClassLoader(new AntClassLoader(Thread.currentThread().getContextClassLoader(), project,
241 classpath, parentFirst));
242 }
243 try {
244
245
246
247
248
249 configuration.prependClasspath(project.getBaseDir().toString());
250 if (auxClasspath != null) {
251 project.log("Using auxclasspath: " + auxClasspath, Project.MSG_VERBOSE);
252 configuration.prependClasspath(auxClasspath.toString());
253 }
254 } catch (IOException ioe) {
255 throw new BuildException(ioe.getMessage(), ioe);
256 }
257 }
258
259 public void execute() throws BuildException {
260 final Handler antLogHandler = new AntLogHandler(project);
261 final ScopedLogHandlersManager logManager = new ScopedLogHandlersManager(Level.FINEST, antLogHandler);
262 try {
263 doTask();
264 } finally {
265 logManager.close();
266 }
267 }
268
269 private void logRulesUsed(RuleSets rules) {
270 project.log("Using these rulesets: " + configuration.getRuleSets(), Project.MSG_VERBOSE);
271
272 RuleSet[] ruleSets = rules.getAllRuleSets();
273 for (RuleSet ruleSet : ruleSets) {
274 for (Rule rule : ruleSet.getRules()) {
275 project.log("Using rule " + rule.getName(), Project.MSG_VERBOSE);
276 }
277 }
278 }
279 }