1
2
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
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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 boolean skipLexicalErrors;
50 private boolean skipDuplicateFiles;
51 private File outputFile;
52 private String encoding = System.getProperty("file.encoding");
53 private List<FileSet> filesets = new ArrayList<FileSet>();
54
55 public void execute() throws BuildException {
56 try {
57 validateFields();
58
59 log("Starting run, minimumTokenCount is " + minimumTokenCount, Project.MSG_INFO);
60
61 log("Tokenizing files", Project.MSG_INFO);
62 CPDConfiguration config = new CPDConfiguration();
63 config.setMinimumTileSize(minimumTokenCount);
64 config.setLanguage(createLanguage());
65 config.setEncoding(encoding);
66 config.setSkipDuplicates(skipDuplicateFiles);
67 config.setSkipLexicalErrors(skipLexicalErrors);
68
69 CPD cpd = new CPD(config);
70 tokenizeFiles(cpd);
71
72 log("Starting to analyze code", Project.MSG_INFO);
73 long timeTaken = analyzeCode(cpd);
74 log("Done analyzing code; that took " + timeTaken + " milliseconds");
75
76 log("Generating report", Project.MSG_INFO);
77 report(cpd);
78 } catch (IOException ioe) {
79 log(ioe.toString(), Project.MSG_ERR);
80 throw new BuildException("IOException during task execution", ioe);
81 } catch (ReportException re) {
82 re.printStackTrace();
83 log(re.toString(), Project.MSG_ERR);
84 throw new BuildException("ReportException during task execution", re);
85 }
86 }
87
88 private Language createLanguage() {
89 Properties p = new Properties();
90 if (ignoreLiterals) {
91 p.setProperty(JavaTokenizer.IGNORE_LITERALS, "true");
92 }
93 if (ignoreIdentifiers) {
94 p.setProperty(JavaTokenizer.IGNORE_IDENTIFIERS, "true");
95 }
96 if (ignoreAnnotations) {
97 p.setProperty(JavaTokenizer.IGNORE_ANNOTATIONS, "true");
98 }
99 return new LanguageFactory().createLanguage(language, p);
100 }
101
102 private void report(CPD cpd) throws ReportException {
103 if (!cpd.getMatches().hasNext()) {
104 log("No duplicates over " + minimumTokenCount + " tokens found", Project.MSG_INFO);
105 }
106 Renderer renderer = createRenderer();
107 FileReporter reporter;
108 if (outputFile == null) {
109 reporter = new FileReporter(encoding);
110 } else if (outputFile.isAbsolute()) {
111 reporter = new FileReporter(outputFile, encoding);
112 } else {
113 reporter = new FileReporter(new File(getProject().getBaseDir(), outputFile.toString()), encoding);
114 }
115 reporter.report(renderer.render(cpd.getMatches()));
116 }
117
118 private void tokenizeFiles(CPD cpd) throws IOException {
119 for (FileSet fileSet: filesets) {
120 DirectoryScanner directoryScanner = fileSet.getDirectoryScanner(getProject());
121 String[] includedFiles = directoryScanner.getIncludedFiles();
122 for (int i = 0; i < includedFiles.length; i++) {
123 File file = new File(directoryScanner.getBasedir() + System.getProperty("file.separator") + includedFiles[i]);
124 log("Tokenizing " + file.getAbsolutePath(), Project.MSG_VERBOSE);
125 cpd.add(file);
126 }
127 }
128 }
129
130 private long analyzeCode(CPD cpd) {
131 long start = System.currentTimeMillis();
132 cpd.go();
133 long stop = System.currentTimeMillis();
134 return stop - start;
135 }
136
137 private Renderer createRenderer() {
138 if (format.equals(TEXT_FORMAT)) {
139 return new SimpleRenderer();
140 } else if (format.equals(CSV_FORMAT)) {
141 return new CSVRenderer();
142 }
143 return new XMLRenderer();
144 }
145
146 private void validateFields() throws BuildException {
147 if (minimumTokenCount == 0) {
148 throw new BuildException("minimumTokenCount is required and must be greater than zero");
149 } else if (filesets.isEmpty()) {
150 throw new BuildException("Must include at least one FileSet");
151 }
152 }
153
154 public void addFileset(FileSet set) {
155 filesets.add(set);
156 }
157
158 public void setMinimumTokenCount(int minimumTokenCount) {
159 this.minimumTokenCount = minimumTokenCount;
160 }
161
162 public void setIgnoreLiterals(boolean value) {
163 this.ignoreLiterals = value;
164 }
165
166 public void setIgnoreIdentifiers(boolean value) {
167 this.ignoreIdentifiers = value;
168 }
169
170 public void setIgnoreAnnotations(boolean value) {
171 this.ignoreAnnotations = value;
172 }
173
174 public void setSkipLexicalErrors(boolean skipLexicalErrors) {
175 this.skipLexicalErrors = skipLexicalErrors;
176 }
177
178 public void setSkipDuplicateFiles(boolean skipDuplicateFiles) {
179 this.skipDuplicateFiles = skipDuplicateFiles;
180 }
181
182 public void setOutputFile(File outputFile) {
183 this.outputFile = outputFile;
184 }
185
186 public void setFormat(FormatAttribute formatAttribute) {
187 format = formatAttribute.getValue();
188 }
189
190 public void setLanguage(LanguageAttribute languageAttribute) {
191 language = languageAttribute.getValue();
192 }
193
194 public void setEncoding(String encodingValue) {
195 encoding = encodingValue;
196 }
197
198 public static class FormatAttribute extends EnumeratedAttribute {
199 private static final String[] FORMATS = new String[]{XML_FORMAT, TEXT_FORMAT, CSV_FORMAT};
200 public String[] getValues() {
201 return FORMATS;
202 }
203 }
204
205 public static class LanguageAttribute extends EnumeratedAttribute {
206 public String[] getValues() {
207 return LanguageFactory.supportedLanguages;
208 }
209 }
210 }