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 }