/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.ant; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMDException; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSetNotFoundException; import net.sourceforge.pmd.TargetJDK1_3; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.renderers.TextRenderer; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Reference; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class PMDTask extends Task { private Path classpath; private List formatters = new ArrayList(); private List filesets = new ArrayList(); private boolean shortFilenames; private boolean printToConsole; private String ruleSetFiles; private String encoding = System.getProperty("file.encoding"); private boolean failOnError; private boolean failOnRuleViolation; private boolean targetJDK13; private String failuresPropertyName; /** * The end of line string for this machine. */ protected String EOL = System.getProperty("line.separator", "\n"); public void setShortFilenames(boolean value) { this.shortFilenames = value; } public void setTargetJDK13(boolean value) { this.targetJDK13 = value; } public void setFailOnError(boolean fail) { this.failOnError = fail; } public void setFailOnRuleViolation(boolean fail) { this.failOnRuleViolation = fail; } public void setPrintToConsole(boolean printToConsole) { this.printToConsole = printToConsole; } public void setRuleSetFiles(String ruleSetFiles) { this.ruleSetFiles = ruleSetFiles; } public void setEncoding(String encoding) { this.encoding = encoding; } public void setFailuresPropertyName(String failuresPropertyName) { this.failuresPropertyName = failuresPropertyName; } public void addFileset(FileSet set) { filesets.add(set); } public void addFormatter(Formatter f) { formatters.add(f); } public void setClasspath(Path classpath) { this.classpath = classpath; } public Path getClasspath() { return classpath; } public Path createClasspath() { if (classpath == null) { classpath = new Path(getProject()); } return classpath.createPath(); } public void setClasspathRef(Reference r) { createLongClasspath().setRefid(r); } public void execute() throws BuildException { validate(); RuleSet rules; try { RuleSetFactory ruleSetFactory = new RuleSetFactory(); if (classpath == null) { log("Using the normal ClassLoader", Project.MSG_VERBOSE); rules = ruleSetFactory.createRuleSet(ruleSetFiles); } else { log("Using the AntClassLoader", Project.MSG_VERBOSE); rules = ruleSetFactory.createRuleSet(ruleSetFiles, new AntClassLoader(getProject(), classpath)); } } catch (RuleSetNotFoundException e) { throw new BuildException(e.getMessage()); } logRulesUsed(rules); PMD pmd; if (targetJDK13) { pmd = new PMD(new TargetJDK1_3()); } else { pmd = new PMD(); } RuleContext ctx = new RuleContext(); ctx.setReport(new Report()); for (Iterator i = filesets.iterator(); i.hasNext();) { FileSet fs = (FileSet) i.next(); DirectoryScanner ds = fs.getDirectoryScanner(getProject()); String[] srcFiles = ds.getIncludedFiles(); for (int j = 0; j < srcFiles.length; j++) { File file = new File(ds.getBasedir() + System.getProperty("file.separator") + srcFiles[j]); log("Processing file " + file.getAbsoluteFile().toString(), Project.MSG_VERBOSE); ctx.setSourceCodeFilename(shortFilenames ? srcFiles[j] : file.getAbsolutePath()); try { pmd.processFile(new FileInputStream(file), encoding, rules, ctx); } catch (FileNotFoundException fnfe) { if (failOnError) { throw new BuildException(fnfe); } } catch (PMDException pmde) { log(pmde.toString(), Project.MSG_VERBOSE); if (pmde.getReason() != null && pmde.getReason().getMessage() != null) { log(pmde.getReason().getMessage(), Project.MSG_VERBOSE); } if (failOnError) { throw new BuildException(pmde); } ctx.getReport().addError(new Report.ProcessingError(pmde.getMessage(), ctx.getSourceCodeFilename())); } } } log(ctx.getReport().size() + " problems found", Project.MSG_VERBOSE); if (!ctx.getReport().isEmpty()) { for (Iterator i = formatters.iterator(); i.hasNext();) { Formatter formatter = (Formatter) i.next(); log("Sending a report to " + formatter, Project.MSG_VERBOSE); String buffer = formatter.getRenderer().render(ctx.getReport()) + EOL; try { Writer writer = formatter.getToFileWriter(getProject().getBaseDir().toString()); writer.write(buffer, 0, buffer.length()); writer.close(); } catch (IOException ioe) { throw new BuildException(ioe.getMessage()); } } if (failuresPropertyName != null && ctx.getReport().size() > 0) { getProject().setProperty(failuresPropertyName, String.valueOf(ctx.getReport().size())); log("Setting property " + failuresPropertyName + " to " + String.valueOf(ctx.getReport().size()), Project.MSG_VERBOSE); } if (printToConsole) { Renderer r = new TextRenderer(); log(r.render(ctx.getReport()), Project.MSG_INFO); } if (failOnRuleViolation) { throw new BuildException("Stopping build since PMD found " + ctx.getReport().size() + " rule violations in the code"); } } } private void logRulesUsed(RuleSet rules) { log("Using these rulesets: " + ruleSetFiles, Project.MSG_VERBOSE); for (Iterator i = rules.getRules().iterator();i.hasNext();) { Rule rule = (Rule)i.next(); log("Using rule " + rule.getName(), Project.MSG_VERBOSE); } } private void validate() throws BuildException { if (formatters.isEmpty() && !printToConsole) { throw new BuildException("No formatter specified; and printToConsole was false"); } for (Iterator i = formatters.iterator(); i.hasNext();) { Formatter f = (Formatter) i.next(); if (f.isToFileNull()) { throw new BuildException("Formatter toFile attribute is required"); } } if (ruleSetFiles == null) { throw new BuildException("No rulesets specified"); } } private Path createLongClasspath() { if (classpath == null) { classpath = new Path(getProject()); } return classpath.createPath(); } }