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 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 }