001: /**
002: * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003: */package net.sourceforge.pmd.ant;
004:
005: import java.io.File;
006: import java.io.PrintWriter;
007: import java.io.StringWriter;
008: import java.io.Writer;
009: import java.util.ArrayList;
010: import java.util.Collection;
011: import java.util.Iterator;
012: import java.util.LinkedList;
013: import java.util.List;
014: import java.util.concurrent.atomic.AtomicInteger;
015: import java.util.logging.Handler;
016: import java.util.logging.Level;
017:
018: import net.sourceforge.pmd.DataSource;
019: import net.sourceforge.pmd.FileDataSource;
020: import net.sourceforge.pmd.PMD;
021: import net.sourceforge.pmd.Report;
022: import net.sourceforge.pmd.Rule;
023: import net.sourceforge.pmd.RuleContext;
024: import net.sourceforge.pmd.RuleSet;
025: import net.sourceforge.pmd.RuleSetFactory;
026: import net.sourceforge.pmd.RuleSetNotFoundException;
027: import net.sourceforge.pmd.RuleSets;
028: import net.sourceforge.pmd.SimpleRuleSetNameMapper;
029: import net.sourceforge.pmd.SourceType;
030: import net.sourceforge.pmd.renderers.AbstractRenderer;
031: import net.sourceforge.pmd.renderers.Renderer;
032: import net.sourceforge.pmd.ScopedLogHandlersManager;
033: import net.sourceforge.pmd.util.AntLogHandler;
034:
035: import org.apache.tools.ant.AntClassLoader;
036: import org.apache.tools.ant.BuildException;
037: import org.apache.tools.ant.DirectoryScanner;
038: import org.apache.tools.ant.Project;
039: import org.apache.tools.ant.Task;
040: import org.apache.tools.ant.types.FileSet;
041: import org.apache.tools.ant.types.Path;
042: import org.apache.tools.ant.types.Reference;
043:
044: public class PMDTask extends Task {
045:
046: private Path classpath;
047: private List<Formatter> formatters = new ArrayList<Formatter>();
048: private List<FileSet> filesets = new ArrayList<FileSet>();
049: private int minPriority = Rule.LOWEST_PRIORITY;
050: private boolean shortFilenames;
051: private String ruleSetFiles;
052: private String encoding = System.getProperty("file.encoding");
053: private boolean failOnError;
054: private boolean failOnRuleViolation;
055: private String targetJDK = "1.5";
056: private String failuresPropertyName;
057: private String excludeMarker = PMD.EXCLUDE_MARKER;
058: private int cpus = Runtime.getRuntime().availableProcessors();
059: private final Collection<RuleSetWrapper> nestedRules = new ArrayList<RuleSetWrapper>();
060:
061: public void setShortFilenames(boolean value) {
062: this .shortFilenames = value;
063: }
064:
065: public void setTargetJDK(String value) {
066: this .targetJDK = value;
067: }
068:
069: public void setExcludeMarker(String value) {
070: this .excludeMarker = value;
071: }
072:
073: public void setFailOnError(boolean fail) {
074: this .failOnError = fail;
075: }
076:
077: public void setFailOnRuleViolation(boolean fail) {
078: this .failOnRuleViolation = fail;
079: }
080:
081: public void setRuleSetFiles(String ruleSetFiles) {
082: this .ruleSetFiles = ruleSetFiles;
083: }
084:
085: public void setEncoding(String encoding) {
086: this .encoding = encoding;
087: }
088:
089: public void setCpus(int cpus) {
090: this .cpus = cpus;
091: }
092:
093: public void setFailuresPropertyName(String failuresPropertyName) {
094: this .failuresPropertyName = failuresPropertyName;
095: }
096:
097: public void setMinimumPriority(int minPriority) {
098: this .minPriority = minPriority;
099: }
100:
101: public void addFileset(FileSet set) {
102: filesets.add(set);
103: }
104:
105: public void addFormatter(Formatter f) {
106: formatters.add(f);
107: }
108:
109: public void setClasspath(Path classpath) {
110: this .classpath = classpath;
111: }
112:
113: public Path getClasspath() {
114: return classpath;
115: }
116:
117: public Path createClasspath() {
118: if (classpath == null) {
119: classpath = new Path(getProject());
120: }
121: return classpath.createPath();
122: }
123:
124: public void setClasspathRef(Reference r) {
125: createLongClasspath().setRefid(r);
126: }
127:
128: private void doTask() {
129: ruleSetFiles = new SimpleRuleSetNameMapper(ruleSetFiles)
130: .getRuleSets();
131:
132: RuleSetFactory ruleSetFactory = new RuleSetFactory() {
133: public RuleSets createRuleSets(String ruleSetFileNames)
134: throws RuleSetNotFoundException {
135: if (classpath == null) {
136: return super .createRuleSets(ruleSetFiles);
137: } else {
138: return createRuleSets(ruleSetFiles,
139: new AntClassLoader(getProject(), classpath));
140:
141: }
142: }
143: };
144: for (Formatter formatter : formatters) {
145: log("Sending a report to " + formatter, Project.MSG_VERBOSE);
146: formatter.start(getProject().getBaseDir().toString());
147: }
148:
149: try {
150: // This is just used to validate and display rules. Each thread will create its own ruleset
151: RuleSets rules;
152: ruleSetFactory.setMinimumPriority(minPriority);
153: if (classpath == null) {
154: log("Using the normal ClassLoader", Project.MSG_VERBOSE);
155: rules = ruleSetFactory.createRuleSets(ruleSetFiles);
156: } else {
157: log("Using the AntClassLoader", Project.MSG_VERBOSE);
158: rules = ruleSetFactory.createRuleSets(ruleSetFiles,
159: new AntClassLoader(getProject(), classpath));
160: }
161: logRulesUsed(rules);
162: } catch (RuleSetNotFoundException e) {
163: throw new BuildException(e.getMessage());
164: }
165:
166: SourceType sourceType;
167: if (targetJDK.equals("1.3")) {
168: log("Targeting Java language version 1.3",
169: Project.MSG_VERBOSE);
170: sourceType = SourceType.JAVA_13;
171: } else if (targetJDK.equals("1.5")) {
172: log("Targeting Java language version 1.5",
173: Project.MSG_VERBOSE);
174: sourceType = SourceType.JAVA_15;
175: } else if (targetJDK.equals("1.6")) {
176: log("Targeting Java language version 1.6",
177: Project.MSG_VERBOSE);
178: sourceType = SourceType.JAVA_16;
179: } else if (targetJDK.equals("1.7")) {
180: log("Targeting Java language version 1.7",
181: Project.MSG_VERBOSE);
182: sourceType = SourceType.JAVA_17;
183: } else if (targetJDK.equals("jsp")) {
184: log("Targeting JSP", Project.MSG_VERBOSE);
185: sourceType = SourceType.JSP;
186: } else {
187: log("Targeting Java language version 1.4",
188: Project.MSG_VERBOSE);
189: sourceType = SourceType.JAVA_14;
190: }
191:
192: if (excludeMarker != null) {
193: log("Setting exclude marker to be " + excludeMarker,
194: Project.MSG_VERBOSE);
195: }
196:
197: RuleContext ctx = new RuleContext();
198: Report errorReport = new Report();
199: final AtomicInteger reportSize = new AtomicInteger();
200: for (FileSet fs : filesets) {
201: List<DataSource> files = new LinkedList<DataSource>();
202: DirectoryScanner ds = fs.getDirectoryScanner(getProject());
203: String[] srcFiles = ds.getIncludedFiles();
204: for (int j = 0; j < srcFiles.length; j++) {
205: File file = new File(ds.getBasedir()
206: + System.getProperty("file.separator")
207: + srcFiles[j]);
208: files.add(new FileDataSource(file));
209: }
210:
211: final String inputPath = ds.getBasedir().getPath();
212:
213: Renderer logRenderer = new AbstractRenderer() {
214: public void start() {
215: }
216:
217: public void startFileAnalysis(DataSource dataSource) {
218: log("Processing file "
219: + dataSource.getNiceFileName(false,
220: inputPath), Project.MSG_VERBOSE);
221: }
222:
223: public void renderFileReport(Report r) {
224: int size = r.size();
225: if (size > 0) {
226: reportSize.addAndGet(size);
227: }
228: }
229:
230: public void end() {
231: }
232:
233: public void render(Writer writer, Report r) {
234: }
235: };
236: List<Renderer> renderers = new LinkedList<Renderer>();
237: renderers.add(logRenderer);
238: for (Formatter formatter : formatters) {
239: renderers.add(formatter.getRenderer());
240: }
241: try {
242: PMD.processFiles(cpus, ruleSetFactory, sourceType,
243: files, ctx, renderers, ruleSetFiles,
244: shortFilenames, inputPath, encoding,
245: excludeMarker, getClass().getClassLoader());
246: } catch (RuntimeException pmde) {
247: pmde.printStackTrace();
248: log(pmde.toString(), Project.MSG_VERBOSE);
249: if (pmde.getCause() != null) {
250: StringWriter strWriter = new StringWriter();
251: PrintWriter printWriter = new PrintWriter(strWriter);
252: pmde.getCause().printStackTrace(printWriter);
253: log(strWriter.toString(), Project.MSG_VERBOSE);
254: }
255: if (pmde.getCause() != null
256: && pmde.getCause().getMessage() != null) {
257: log(pmde.getCause().getMessage(),
258: Project.MSG_VERBOSE);
259: }
260: if (failOnError) {
261: throw new BuildException(pmde);
262: }
263: errorReport.addError(new Report.ProcessingError(pmde
264: .getMessage(), ctx.getSourceCodeFilename()));
265: }
266: }
267:
268: int problemCount = reportSize.get();
269: log(problemCount + " problems found", Project.MSG_VERBOSE);
270:
271: for (Formatter formatter : formatters) {
272: formatter.end(errorReport);
273: }
274:
275: if (failuresPropertyName != null && problemCount > 0) {
276: getProject().setProperty(failuresPropertyName,
277: String.valueOf(problemCount));
278: log("Setting property " + failuresPropertyName + " to "
279: + problemCount, Project.MSG_VERBOSE);
280: }
281:
282: if (failOnRuleViolation && problemCount > 0) {
283: throw new BuildException("Stopping build since PMD found "
284: + problemCount + " rule violations in the code");
285: }
286: }
287:
288: public void execute() throws BuildException {
289: validate();
290: final Handler antLogHandler = new AntLogHandler(this );
291: final ScopedLogHandlersManager logManager = new ScopedLogHandlersManager(
292: Level.FINEST, antLogHandler);
293: try {
294: doTask();
295: } finally {
296: logManager.close();
297: }
298: }
299:
300: private void logRulesUsed(RuleSets rules) {
301: log("Using these rulesets: " + ruleSetFiles,
302: Project.MSG_VERBOSE);
303:
304: RuleSet[] ruleSets = rules.getAllRuleSets();
305: for (int j = 0; j < ruleSets.length; j++) {
306: RuleSet ruleSet = ruleSets[j];
307:
308: for (Rule rule : ruleSet.getRules()) {
309: log("Using rule " + rule.getName(), Project.MSG_VERBOSE);
310: }
311: }
312: }
313:
314: private void validate() throws BuildException {
315: // TODO - check for empty Formatters List here?
316: for (Formatter f : formatters) {
317: if (f.isNoOutputSupplied()) {
318: throw new BuildException(
319: "toFile or toConsole needs to be specified in Formatter");
320: }
321: }
322:
323: if (ruleSetFiles == null) {
324: if (nestedRules.isEmpty()) {
325: throw new BuildException("No rulesets specified");
326: }
327: ruleSetFiles = getNestedRuleSetFiles();
328: }
329:
330: if (!targetJDK.equals("1.3") && !targetJDK.equals("1.4")
331: && !targetJDK.equals("1.5") && !targetJDK.equals("1.6")
332: && !targetJDK.equals("1.7") && !targetJDK.equals("jsp")) {
333: throw new BuildException(
334: "The targetjdk attribute, if used, must be set to either '1.3', '1.4', '1.5', '1.6', '1.7' or 'jsp'");
335: }
336: }
337:
338: private String getNestedRuleSetFiles() {
339: final StringBuffer sb = new StringBuffer();
340: for (Iterator<RuleSetWrapper> it = nestedRules.iterator(); it
341: .hasNext();) {
342: RuleSetWrapper rs = it.next();
343: sb.append(rs.getFile());
344: if (it.hasNext()) {
345: sb.append(',');
346: }
347: }
348: return sb.toString();
349: }
350:
351: private Path createLongClasspath() {
352: if (classpath == null) {
353: classpath = new Path(getProject());
354: }
355: return classpath.createPath();
356: }
357:
358: public void addRuleset(RuleSetWrapper r) {
359: nestedRules.add(r);
360: }
361:
362: }
|