1 /*** 2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html 3 */ 4 package net.sourceforge.pmd.cpd; 5 6 import org.apache.tools.ant.BuildException; 7 import org.apache.tools.ant.DirectoryScanner; 8 import org.apache.tools.ant.Project; 9 import org.apache.tools.ant.Task; 10 import org.apache.tools.ant.types.EnumeratedAttribute; 11 import org.apache.tools.ant.types.FileSet; 12 13 import java.io.File; 14 import java.io.IOException; 15 import java.util.ArrayList; 16 import java.util.Iterator; 17 import java.util.List; 18 19 /*** 20 * CPDTask 21 * 22 * Runs the CPD utility via ant. The ant task looks like this: 23 * 24 * <project name="CPDProj" default="main" basedir="."> 25 * <taskdef name="cpd" classname="net.sourceforge.pmd.cpd.CPDTask" /> 26 * <target name="main"> 27 * <cpd minimumTokenCount="100" outputFile="c:\cpdrun.txt"> 28 * <fileset dir="/path/to/my/src"> 29 * <include name="*.java"/> 30 * </fileset> 31 * </cpd> 32 * </target> 33 *</project> 34 * 35 * Required: minimumTokenCount, outputFile, and at least one file 36 */ 37 public class CPDTask extends Task { 38 39 private static final String TEXT_FORMAT = "text"; 40 private static final String XML_FORMAT = "xml"; 41 private static final String CSV_FORMAT = "csv"; 42 43 private String format = TEXT_FORMAT; 44 private int minimumTokenCount; 45 private File outputFile; 46 private List filesets = new ArrayList(); 47 48 public void execute() throws BuildException { 49 try { 50 validateFields(); 51 52 log("Tokenizing files", Project.MSG_INFO); 53 CPD cpd = new CPD(minimumTokenCount, new JavaLanguage()); 54 tokenizeFiles(cpd); 55 56 log("Starting to analyze code", Project.MSG_INFO); 57 long timeTaken = analyzeCode(cpd); 58 log("Done analyzing code; that took " + timeTaken + " milliseconds"); 59 60 log("Generating report", Project.MSG_INFO); 61 report(cpd); 62 } catch (IOException ioe) { 63 log(ioe.toString(), Project.MSG_ERR); 64 throw new BuildException("IOException during task execution", ioe); 65 } catch (ReportException re) { 66 log(re.toString(), Project.MSG_ERR); 67 throw new BuildException("ReportException during task execution", re); 68 } 69 } 70 71 private void report(CPD cpd) throws ReportException { 72 if (!cpd.getMatches().hasNext()) { 73 log("No duplicates over " + minimumTokenCount + " tokens found", Project.MSG_INFO); 74 } 75 Renderer renderer = createRenderer(); 76 if (outputFile.isAbsolute()) { 77 new FileReporter(outputFile).report(renderer.render(cpd.getMatches())); 78 } else { 79 new FileReporter(new File(getProject().getBaseDir(), outputFile.toString())); 80 } 81 } 82 83 84 private void tokenizeFiles(CPD cpd) throws IOException { 85 for (Iterator iterator = filesets.iterator(); iterator.hasNext();) { 86 FileSet fileSet = (FileSet) iterator.next(); 87 DirectoryScanner directoryScanner = fileSet.getDirectoryScanner(getProject()); 88 String[] includedFiles = directoryScanner.getIncludedFiles(); 89 for (int i = 0; i < includedFiles.length; i++) { 90 File file = new File(directoryScanner.getBasedir() + System.getProperty("file.separator") + includedFiles[i]); 91 log("Tokenizing " + file.getAbsolutePath(), Project.MSG_VERBOSE); 92 cpd.add(file); 93 } 94 } 95 } 96 97 private long analyzeCode(CPD cpd) { 98 long start = System.currentTimeMillis(); 99 cpd.go(); 100 long stop = System.currentTimeMillis(); 101 return stop - start; 102 } 103 104 private Renderer createRenderer() { 105 if (format.equals(TEXT_FORMAT)) { 106 return new SimpleRenderer(); 107 } else if (format.equals(CSV_FORMAT)) { 108 return new CSVRenderer(); 109 } 110 return new XMLRenderer(); 111 } 112 113 private void validateFields() throws BuildException{ 114 if(minimumTokenCount == 0){ 115 throw new BuildException("minimumTokenCount is required and must be greater than zero"); 116 } else if(outputFile == null) { 117 throw new BuildException("outputFile is a required attribute"); 118 } else if (filesets.isEmpty()) { 119 throw new BuildException("Must include at least one FileSet"); 120 } 121 122 } 123 124 public void addFileset(FileSet set) { 125 filesets.add(set); 126 } 127 128 public void setMinimumTokenCount(int minimumTokenCount) { 129 this.minimumTokenCount = minimumTokenCount; 130 } 131 132 public void setOutputFile(File outputFile) { 133 this.outputFile = outputFile; 134 } 135 136 public void setFormat(FormatAttribute formatAttribute) { 137 format = formatAttribute.getValue(); 138 } 139 140 public static class FormatAttribute extends EnumeratedAttribute { 141 private String[] formats = new String[] {XML_FORMAT, TEXT_FORMAT, CSV_FORMAT}; 142 143 public String[] getValues() { 144 return formats; 145 } 146 } 147 }