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.SimpleRuleSetNameMapper; 15 import net.sourceforge.pmd.TargetJDK1_3; 16 import net.sourceforge.pmd.TargetJDK1_5; 17 import net.sourceforge.pmd.renderers.Renderer; 18 import net.sourceforge.pmd.renderers.TextRenderer; 19 import org.apache.tools.ant.AntClassLoader; 20 import org.apache.tools.ant.BuildException; 21 import org.apache.tools.ant.DirectoryScanner; 22 import org.apache.tools.ant.Project; 23 import org.apache.tools.ant.Task; 24 import org.apache.tools.ant.types.FileSet; 25 import org.apache.tools.ant.types.Path; 26 import org.apache.tools.ant.types.Reference; 27 28 import java.io.BufferedInputStream; 29 import java.io.File; 30 import java.io.FileInputStream; 31 import java.io.FileNotFoundException; 32 import java.io.IOException; 33 import java.io.PrintWriter; 34 import java.io.StringWriter; 35 import java.io.Writer; 36 import java.util.ArrayList; 37 import java.util.Collection; 38 import java.util.Iterator; 39 import java.util.List; 40 41 public class PMDTask extends Task { 42 43 private Path classpath; 44 private List formatters = new ArrayList(); 45 private List filesets = new ArrayList(); 46 private boolean shortFilenames; 47 private boolean printToConsole; 48 private String ruleSetFiles; 49 private String encoding = System.getProperty("file.encoding"); 50 private boolean failOnError; 51 private boolean failOnRuleViolation; 52 private String targetJDK = "1.4"; 53 private String failuresPropertyName; 54 private String excludeMarker; 55 private final Collection nestedRules = new ArrayList(); 56 57 public void setShortFilenames(boolean value) { 58 this.shortFilenames = value; 59 } 60 61 public void setTargetJDK(String value) { 62 this.targetJDK = value; 63 } 64 65 public void setExcludeMarker(String value) { 66 this.excludeMarker = value; 67 } 68 69 public void setFailOnError(boolean fail) { 70 this.failOnError = fail; 71 } 72 73 public void setFailOnRuleViolation(boolean fail) { 74 this.failOnRuleViolation = fail; 75 } 76 77 public void setPrintToConsole(boolean printToConsole) { 78 this.printToConsole = printToConsole; 79 } 80 81 public void setRuleSetFiles(String ruleSetFiles) { 82 this.ruleSetFiles = ruleSetFiles; 83 } 84 85 public void setEncoding(String encoding) { 86 this.encoding = encoding; 87 } 88 89 public void setFailuresPropertyName(String failuresPropertyName) { 90 this.failuresPropertyName = failuresPropertyName; 91 } 92 93 public void addFileset(FileSet set) { 94 filesets.add(set); 95 } 96 97 public void addFormatter(Formatter f) { 98 formatters.add(f); 99 } 100 101 public void setClasspath(Path classpath) { 102 this.classpath = classpath; 103 } 104 105 public Path getClasspath() { 106 return classpath; 107 } 108 109 public Path createClasspath() { 110 if (classpath == null) { 111 classpath = new Path(getProject()); 112 } 113 return classpath.createPath(); 114 } 115 116 public void setClasspathRef(Reference r) { 117 createLongClasspath().setRefid(r); 118 } 119 120 public void execute() throws BuildException { 121 validate(); 122 123 ruleSetFiles = new SimpleRuleSetNameMapper(ruleSetFiles).getRuleSets(); 124 RuleSet rules; 125 try { 126 RuleSetFactory ruleSetFactory = new RuleSetFactory(); 127 if (classpath == null) { 128 log("Using the normal ClassLoader", Project.MSG_VERBOSE); 129 rules = ruleSetFactory.createRuleSet(ruleSetFiles); 130 } else { 131 log("Using the AntClassLoader", Project.MSG_VERBOSE); 132 rules = ruleSetFactory.createRuleSet(ruleSetFiles, new AntClassLoader(getProject(), classpath)); 133 } 134 } catch (RuleSetNotFoundException e) { 135 throw new BuildException(e.getMessage()); 136 } 137 logRulesUsed(rules); 138 139 PMD pmd; 140 if (targetJDK.equals("1.3")) { 141 pmd = new PMD(new TargetJDK1_3()); 142 } else if (targetJDK.equals("1.5")) { 143 pmd = new PMD(new TargetJDK1_5()); 144 } else { 145 pmd = new PMD(); 146 } 147 if (excludeMarker != null) { 148 log("Setting exclude marker to be " + excludeMarker, Project.MSG_VERBOSE); 149 pmd.setExcludeMarker(excludeMarker); 150 } 151 152 RuleContext ctx = new RuleContext(); 153 Report report = new Report(); 154 ctx.setReport(report); 155 report.start(); 156 for (Iterator i = filesets.iterator(); i.hasNext();) { 157 FileSet fs = (FileSet) i.next(); 158 DirectoryScanner ds = fs.getDirectoryScanner(getProject()); 159 String[] srcFiles = ds.getIncludedFiles(); 160 for (int j = 0; j < srcFiles.length; j++) { 161 File file = new File(ds.getBasedir() + System.getProperty("file.separator") + srcFiles[j]); 162 log("Processing file " + file.getAbsoluteFile().toString(), Project.MSG_VERBOSE); 163 ctx.setSourceCodeFilename(shortFilenames ? srcFiles[j] : file.getAbsolutePath()); 164 try { 165 pmd.processFile(new BufferedInputStream(new FileInputStream(file)), encoding, rules, ctx); 166 } catch (FileNotFoundException fnfe) { 167 if (failOnError) { 168 throw new BuildException(fnfe); 169 } 170 } catch (PMDException pmde) { 171 log(pmde.toString(), Project.MSG_VERBOSE); 172 if (pmde.getReason() != null) { 173 StringWriter strWriter = new StringWriter(); 174 PrintWriter printWriter = new PrintWriter(strWriter); 175 pmde.getReason().printStackTrace(printWriter); 176 log(strWriter.toString(), Project.MSG_VERBOSE); 177 } 178 if (pmde.getReason() != null && pmde.getReason().getMessage() != null) { 179 log(pmde.getReason().getMessage(), Project.MSG_VERBOSE); 180 } 181 if (failOnError) { 182 throw new BuildException(pmde); 183 } 184 ctx.getReport().addError(new Report.ProcessingError(pmde.getMessage(), ctx.getSourceCodeFilename())); 185 } 186 } 187 } 188 report.end(); 189 190 log(ctx.getReport().size() + " problems found", Project.MSG_VERBOSE); 191 192 for (Iterator i = formatters.iterator(); i.hasNext();) { 193 Formatter formatter = (Formatter) i.next(); 194 log("Sending a report to " + formatter, Project.MSG_VERBOSE); 195 String buffer = formatter.getRenderer().render(ctx.getReport()) + PMD.EOL; 196 try { 197 Writer writer = formatter.getToFileWriter(getProject().getBaseDir().toString()); 198 writer.write(buffer, 0, buffer.length()); 199 writer.close(); 200 } catch (IOException ioe) { 201 throw new BuildException(ioe.getMessage()); 202 } 203 } 204 205 if (failuresPropertyName != null && ctx.getReport().size() > 0) { 206 getProject().setProperty(failuresPropertyName, String.valueOf(ctx.getReport().size())); 207 log("Setting property " + failuresPropertyName + " to " + String.valueOf(ctx.getReport().size()), Project.MSG_VERBOSE); 208 } 209 210 if (printToConsole) { 211 Renderer r = new TextRenderer(); 212 log(r.render(ctx.getReport()), Project.MSG_INFO); 213 } 214 215 if (failOnRuleViolation && ctx.getReport().size() > 0) { 216 throw new BuildException("Stopping build since PMD found " + ctx.getReport().size() + " rule violations in the code"); 217 } 218 } 219 220 private void logRulesUsed(net.sourceforge.pmd.RuleSet rules) { 221 log("Using these rulesets: " + ruleSetFiles, Project.MSG_VERBOSE); 222 for (Iterator i = rules.getRules().iterator(); i.hasNext();) { 223 Rule rule = (Rule) i.next(); 224 log("Using rule " + rule.getName(), Project.MSG_VERBOSE); 225 } 226 } 227 228 private void validate() throws BuildException { 229 if (formatters.isEmpty() && !printToConsole) { 230 throw new BuildException("No formatter specified; and printToConsole was false"); 231 } 232 233 for (Iterator i = formatters.iterator(); i.hasNext();) { 234 Formatter f = (Formatter) i.next(); 235 if (f.isToFileNull()) { 236 throw new BuildException("Formatter toFile attribute is required"); 237 } 238 } 239 240 if (ruleSetFiles == null) { 241 if (nestedRules.isEmpty()) { 242 throw new BuildException("No rulesets specified"); 243 } 244 ruleSetFiles = getNestedRuleSetFiles(); 245 } 246 247 if (!targetJDK.equals("1.3") && !targetJDK.equals("1.4") && !targetJDK.equals("1.5")) { 248 throw new BuildException("The targetjdk attribute, if used, must be set to either '1.3', '1.4', or '1.5'"); 249 } 250 } 251 252 private String getNestedRuleSetFiles() { 253 final StringBuffer sb = new StringBuffer(); 254 for (Iterator it = nestedRules.iterator() ; it.hasNext() ; ) { 255 RuleSetWrapper rs = (RuleSetWrapper) it.next(); 256 sb.append(rs.getFile()); 257 if (it.hasNext()) { 258 sb.append(','); 259 } 260 } 261 return sb.toString(); 262 } 263 264 private Path createLongClasspath() { 265 if (classpath == null) { 266 classpath = new Path(getProject()); 267 } 268 return classpath.createPath(); 269 } 270 271 public void addRuleset(RuleSetWrapper r) { 272 nestedRules.add(r); 273 } 274 275 }