View Javadoc

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