1 /**
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.dcd;
5
6 import java.io.File;
7 import java.io.FilenameFilter;
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.List;
11
12 import net.sourceforge.pmd.dcd.graph.UsageGraph;
13 import net.sourceforge.pmd.dcd.graph.UsageGraphBuilder;
14 import net.sourceforge.pmd.util.FileFinder;
15 import net.sourceforge.pmd.util.filter.Filter;
16 import net.sourceforge.pmd.util.filter.Filters;
17
18 /**
19 * The Dead Code Detector is used to find dead code. What is dead code?
20 * Dead code is code which is not used by other code? It exists, but it not
21 * used. Unused code is clutter, which can generally be a candidate for
22 * removal.
23 * <p>
24 * When performing dead code detection, there are various sets of files/classes
25 * which must be identified. An analogy of the dead code analysis as
26 * a <em>foot race</em> is used to help clarify each of these sets:
27 * <ol>
28 * <li>The <em>direct users</em> is the set of Classes which will always be
29 * parsed to determine what code they use. This set is the starting point of
30 * the race.</li>
31 * <li>The <em>indirect users</em> is the set of Classes which will only be
32 * parsed if they are accessed by code in the <em>direct users</em> set, or
33 * in the <em>indirect users</em> set. This set is the course of the race.</li>
34 * <li>The <em>dead code candidates</em> are the set of Classes which are the
35 * focus of the dead code detection. This set is the finish line of the
36 * race.</li>
37 * </ol>
38 * <p>
39 * Typically there is intersection between the set of <em>direct users</em>,
40 * <em>indirect users</em> and <em>dead code candidates</em>, although it is
41 * not required. If the sets are defined too tightly, there the potential for
42 * a lot of code to be considered as dead code. You may need to expand the
43 * <em>direct users</em> or <em>indirect users</em> sets, or explore using
44 * different options.
45 *
46 * @author Ryan Gustafson <ryan.gustafson@gmail.com>,
47 */
48 public class DCD {
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83 public static void dump(UsageGraph usageGraph, boolean verbose) {
84 usageGraph.accept(new DumpNodeVisitor(), Boolean.valueOf(verbose));
85 }
86
87 public static void report(UsageGraph usageGraph, boolean verbose) {
88 usageGraph.accept(new UsageNodeVisitor(), Boolean.valueOf(verbose));
89 }
90
91 public static void main(String[] args) throws Exception {
92
93 List<File> directories = new ArrayList<File>();
94 directories.add(new File("C:/pmd/workspace/pmd-trunk/src"));
95
96
97 FilenameFilter javaFilter = new FilenameFilter() {
98 public boolean accept(File dir, String name) {
99
100 if (new File(dir, name).isDirectory()) {
101 return true;
102 } else {
103
104 if (name.startsWith("EJS") || name.startsWith("_") || dir.getPath().indexOf("com\\ibm\\") >= 0
105 || dir.getPath().indexOf("org\\omg\\") >= 0) {
106 return false;
107 }
108 return name.endsWith(".java");
109 }
110 }
111 };
112
113
114 List<FilenameFilter> filters = new ArrayList<FilenameFilter>();
115 filters.add(javaFilter);
116
117 assert (directories.size() == filters.size());
118
119
120 List<String> classes = new ArrayList<String>();
121 for (int i = 0; i < directories.size(); i++) {
122 File directory = directories.get(i);
123 FilenameFilter filter = filters.get(i);
124 List<File> files = new FileFinder().findFilesFrom(directory.getPath(), filter, true);
125 for (File file : files) {
126 String name = file.getPath();
127
128
129 name = name.substring(directory.getPath().length() + 1);
130
131
132 name = name.replaceAll("\\.java$", "");
133
134
135 name = name.replace('\\', '.');
136 name = name.replace('/', '.');
137
138 classes.add(name);
139 }
140 }
141
142 long start = System.currentTimeMillis();
143
144
145
146 List<String> includeRegexes = Arrays.asList(new String[] { "net\\.sourceforge\\.pmd\\.dcd.*", "us\\..*" });
147 List<String> excludeRegexes = Arrays.asList(new String[] { "java\\..*", "javax\\..*", ".*\\.twa\\..*" });
148 Filter<String> classFilter = Filters.buildRegexFilterExcludeOverInclude(includeRegexes, excludeRegexes);
149 System.out.println("Class filter: " + classFilter);
150
151
152 UsageGraphBuilder builder = new UsageGraphBuilder(classFilter);
153 int total = 0;
154 for (String clazz : classes) {
155 System.out.println("indexing class: " + clazz);
156 builder.index(clazz);
157 total++;
158 if (total % 20 == 0) {
159 System.out.println(total + " : " + total / ((System.currentTimeMillis() - start) / 1000.0));
160 }
161 }
162
163
164 boolean dump = true;
165 boolean deadCode = true;
166 UsageGraph usageGraph = builder.getUsageGraph();
167 if (dump) {
168 System.out.println("--- Dump ---");
169 dump(usageGraph, true);
170 }
171 if (deadCode) {
172 System.out.println("--- Dead Code ---");
173 report(usageGraph, true);
174 }
175 long end = System.currentTimeMillis();
176 System.out.println("Time: " + (end - start) / 1000.0);
177 }
178 }