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 net.sourceforge.pmd.SourceFileSelector;
7
8 import java.io.File;
9 import java.io.FileNotFoundException;
10 import java.io.IOException;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Set;
17
18 public class CPD {
19
20 private Map source = new HashMap();
21 private CPDListener listener = new CPDNullListener();
22 private Tokens tokens = new Tokens();
23 private int minimumTileSize;
24 private MatchAlgorithm matchAlgorithm;
25 private Language language;
26 private boolean skipDuplicates;
27
28 public CPD(int minimumTileSize, Language language) {
29 this.minimumTileSize = minimumTileSize;
30 this.language = language;
31 }
32
33 public void skipDuplicates() {
34 this.skipDuplicates = true;
35 }
36
37 public void setCpdListener(CPDListener cpdListener) {
38 this.listener = cpdListener;
39 }
40
41 public void go() {
42 TokenEntry.clearImages();
43 matchAlgorithm = new MatchAlgorithm(source, tokens, minimumTileSize, listener);
44 matchAlgorithm.findMatches();
45 }
46
47 public Iterator getMatches() {
48 return matchAlgorithm.matches();
49 }
50
51 public void add(File file) throws IOException {
52 add(1, file);
53 }
54
55 public void addAllInDirectory(String dir) throws IOException {
56 addDirectory(dir, false);
57 }
58
59 public void addRecursively(String dir) throws IOException {
60 addDirectory(dir, true);
61 }
62
63 public void add(List files) throws IOException {
64 for (Iterator i = files.iterator(); i.hasNext();) {
65 add(files.size(), (File) i.next());
66 }
67 }
68
69 private void addDirectory(String dir, boolean recurse) throws IOException {
70 if (!(new File(dir)).exists()) {
71 throw new FileNotFoundException("Couldn't find directory " + dir);
72 }
73 FileFinder finder = new FileFinder();
74
75 add(finder.findFilesFrom(dir, language.getFileFilter(), recurse));
76 }
77
78 private Set current = new HashSet();
79
80 private void add(int fileCount, File file) throws IOException {
81
82 if (skipDuplicates) {
83
84 String signature = file.getName() + "_" + String.valueOf(file.length());
85 if (current.contains(signature)) {
86 System.out.println("Skipping " + file.getAbsolutePath() + " since it appears to be a duplicate file and --skip-duplicate-files is set");
87 return;
88 }
89 current.add(signature);
90 }
91
92 listener.addedFile(fileCount, file);
93 SourceCode sourceCode = new SourceCode(new SourceCode.FileCodeLoader(file));
94 language.getTokenizer().tokenize(sourceCode, tokens);
95 source.put(sourceCode.getFileName(), sourceCode);
96 }
97
98 public static Renderer getRendererFromString(String name) {
99 if (name.equals("text") || name.equals("")) {
100 return new SimpleRenderer();
101 }
102 try {
103 return (Renderer) Class.forName(name).newInstance();
104 } catch (Exception e) {
105 System.out.println("Can't find class '" + name + "' so defaulting to SimpleRenderer.");
106 }
107 return new SimpleRenderer();
108 }
109
110 private static boolean findBooleanSwitch(String[] args, String name) {
111 for (int i = 0; i < args.length; i++) {
112 if (args[i].equals(name)) {
113 return true;
114 }
115 }
116 return false;
117 }
118
119 private static String findRequiredStringValue(String[] args, String name) {
120 for (int i = 0; i < args.length; i++) {
121 if (args[i].equals(name)) {
122 return args[i + 1];
123 }
124 }
125 System.out.println("No " + name + " value passed in");
126 usage();
127 throw new RuntimeException();
128 }
129
130 private static String findOptionalStringValue(String[] args, String name, String defaultValue) {
131 for (int i = 0; i < args.length; i++) {
132 if (args[i].equals(name)) {
133 return args[i + 1];
134 }
135 }
136 return defaultValue;
137 }
138
139 public static void main(String[] args) {
140 if (args.length == 0) {
141 usage();
142 }
143
144 try {
145 boolean skipDuplicateFiles = findBooleanSwitch(args, "--skip-duplicate-files");
146 String pathToFiles = findRequiredStringValue(args, "--files");
147 String languageString = findOptionalStringValue(args, "--language", "java");
148 String formatString = findOptionalStringValue(args, "--format", "text");
149 int minimumTokens = Integer.parseInt(findRequiredStringValue(args, "--minimum-tokens"));
150 LanguageFactory f = new LanguageFactory();
151 Language language = f.createLanguage(languageString);
152 Renderer renderer = CPD.getRendererFromString(formatString);
153 CPD cpd = new CPD(minimumTokens, language);
154 if (skipDuplicateFiles) {
155 cpd.skipDuplicates();
156 }
157 cpd.addRecursively(pathToFiles);
158 cpd.go();
159 System.out.println(renderer.render(cpd.getMatches()));
160 } catch (Exception e) {
161 e.printStackTrace();
162 }
163 }
164
165 private static void usage() {
166 System.out.println("Usage:");
167 System.out.println(" java net.sourceforge.pmd.cpd.CPD --minimum-tokens xxx --files xxx [--language xxx] [--format (xml|text|csv)] [--skip-duplicate-files] ");
168 System.out.println("i.e: ");
169 System.out.println(" java net.sourceforge.pmd.cpd.CPD --minimum-tokens 100 --files c://jdk14//src//java ");
170 System.out.println("or: ");
171 System.out.println(" java net.sourceforge.pmd.cpd.CPD --minimum-tokens 100 --files /path/to/c/code --language c ");
172 System.out.println("or: ");
173 System.out.println(" java net.sourceforge.pmd.cpd.CPD --minimum-tokens 100 --files /path/to/java/code --format xml");
174 }
175
176 }