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.List;
17  import java.util.Properties;
18  
19  /**
20   * CPDTask
21   * <p/>
22   * Runs the CPD utility via ant. The ant task looks like this:
23   * <p/>
24   * <project name="CPDProj" default="main" basedir=".">
25   * <taskdef name="cpd" classname="net.sourceforge.pmd.cpd.CPDTask" />
26   * <target name="main">
27   * <cpd encoding="UTF-16LE" language="java" ignoreIdentifiers="true" ignoreLiterals="true" ignoreAnnotations="true" 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   * <p/>
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 String language = "java";
45      private int minimumTokenCount;
46      private boolean ignoreLiterals;
47      private boolean ignoreIdentifiers;
48      private boolean ignoreAnnotations;
49      private File outputFile;
50      private String encoding = System.getProperty("file.encoding");
51      private List<FileSet> filesets = new ArrayList<FileSet>();
52  
53      public void execute() throws BuildException {
54          try {
55              validateFields();
56  
57              log("Starting run, minimumTokenCount is " + minimumTokenCount, Project.MSG_INFO);
58  
59              log("Tokenizing files", Project.MSG_INFO);
60              CPDConfiguration config = new CPDConfiguration(
61              		minimumTokenCount, createLanguage(), encoding
62              		);
63              CPD cpd = new CPD(config);
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 Language createLanguage() {
83          Properties p = new Properties();
84          if (ignoreLiterals) {
85              p.setProperty(JavaTokenizer.IGNORE_LITERALS, "true");
86          }
87          if (ignoreIdentifiers) {
88              p.setProperty(JavaTokenizer.IGNORE_IDENTIFIERS, "true");
89          }
90          if (ignoreAnnotations) {
91              p.setProperty(JavaTokenizer.IGNORE_ANNOTATIONS, "true");
92          }
93          return new LanguageFactory().createLanguage(language, p);
94      }
95  
96      private void report(CPD cpd) throws ReportException {
97          if (!cpd.getMatches().hasNext()) {
98              log("No duplicates over " + minimumTokenCount + " tokens found", Project.MSG_INFO);
99          }
100         Renderer renderer = createRenderer();
101         FileReporter reporter;
102         if (outputFile == null) {
103         	reporter = new FileReporter(encoding);
104         } else if (outputFile.isAbsolute()) {
105             reporter = new FileReporter(outputFile, encoding);
106         } else {
107             reporter = new FileReporter(new File(getProject().getBaseDir(), outputFile.toString()), encoding);
108         }
109         reporter.report(renderer.render(cpd.getMatches()));
110     }
111 
112     private void tokenizeFiles(CPD cpd) throws IOException {
113         for (FileSet fileSet: filesets) {
114             DirectoryScanner directoryScanner = fileSet.getDirectoryScanner(getProject());
115             String[] includedFiles = directoryScanner.getIncludedFiles();
116             for (int i = 0; i < includedFiles.length; i++) {
117                 File file = new File(directoryScanner.getBasedir() + System.getProperty("file.separator") + includedFiles[i]);
118                 log("Tokenizing " + file.getAbsolutePath(), Project.MSG_VERBOSE);
119                 cpd.add(file);
120             }
121         }
122     }
123 
124     private long analyzeCode(CPD cpd) {
125         long start = System.currentTimeMillis();
126         cpd.go();
127         long stop = System.currentTimeMillis();
128         return stop - start;
129     }
130 
131     private Renderer createRenderer() {
132         if (format.equals(TEXT_FORMAT)) {
133             return new SimpleRenderer();
134         } else if (format.equals(CSV_FORMAT)) {
135             return new CSVRenderer();
136         }
137         return new XMLRenderer();
138     }
139 
140     private void validateFields() throws BuildException {
141         if (minimumTokenCount == 0) {
142             throw new BuildException("minimumTokenCount is required and must be greater than zero");
143         } else if (filesets.isEmpty()) {
144             throw new BuildException("Must include at least one FileSet");
145         }
146     }
147 
148     public void addFileset(FileSet set) {
149         filesets.add(set);
150     }
151 
152     public void setMinimumTokenCount(int minimumTokenCount) {
153         this.minimumTokenCount = minimumTokenCount;
154     }
155 
156     public void setIgnoreLiterals(boolean value) {
157         this.ignoreLiterals = value;
158     }
159 
160     public void setIgnoreIdentifiers(boolean value) {
161         this.ignoreIdentifiers = value;
162     }
163 
164     public void setIgnoreAnnotations(boolean value) {
165         this.ignoreAnnotations = value;
166     }
167 
168     public void setOutputFile(File outputFile) {
169         this.outputFile = outputFile;
170     }
171 
172     public void setFormat(FormatAttribute formatAttribute) {
173         format = formatAttribute.getValue();
174     }
175 
176     public void setLanguage(LanguageAttribute languageAttribute) {
177         language = languageAttribute.getValue();
178     }
179 
180     public void setEncoding(String encodingValue) {
181         encoding = encodingValue;
182     }
183 
184     public static class FormatAttribute extends EnumeratedAttribute {
185         private static final String[] FORMATS = new String[]{XML_FORMAT, TEXT_FORMAT, CSV_FORMAT};
186         public String[] getValues() {
187             return FORMATS;
188         }
189     }
190 
191     public static class LanguageAttribute extends EnumeratedAttribute {
192         public String[] getValues() {
193             return LanguageFactory.supportedLanguages;
194         }
195     }
196 }