1 /**
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.dcd.graph;
5
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.List;
9
10 import net.sourceforge.pmd.dcd.ClassLoaderUtil;
11 import net.sourceforge.pmd.util.filter.Filter;
12
13 /**
14 * A UsageGraph tracks usage references between Java classes, based upon
15 * a parsing of the class files. Once the UsageGraph is built, it may be
16 * visited to perform additional post-processing, or usage relationship
17 * analysis.
18 * <p>
19 * The UsageGraph is composed of ClassNodes. Each ClassNode has various
20 * MemberNodes, specifically ConstructorNodes, FieldNodes, and MethodNodes.
21 * Each of these MemberNodes keeps track of other MemberNodes which it
22 * <em>uses</em> and other MemberNodes which are <em>users</em> of it. In
23 * this sense, the graph can navigated bi-directionally across the <em>use</em>
24 * relationship between MemberNodes.
25 * <p>
26 * Great effort is taken to keep the bookkeeping of the UsageGraph as tight
27 * as possible, so that rather large code bases can be analyzed. While nodes
28 * can grant access to the underlying Java Reflection APIs (e.g. Class,
29 * Constructor, Field, Member), the results are stored using WeakReferences
30 * to assist with memory usage.
31 * <p>
32 * A class Filter can be specified to limit the set of classes on which
33 * usage references will be tracked. This is often done to limit memory
34 * usage to interesting classes. For example, the <code>java.util</code>
35 * package is very often used, and tracking usages would require a massive
36 * bookkeeping effort which has little value.
37 *
38 * @see UsageGraphBuilder
39 * @see ClassNode
40 * @see MemberNode
41 * @see ConstructorNode
42 * @see FieldNode
43 * @see MethodNode
44 * @see NodeVisitor
45 * @see NodeVisitorAcceptor
46 */
47 public class UsageGraph implements NodeVisitorAcceptor {
48
49 private final List<ClassNode> classNodes = new ArrayList<ClassNode>();
50
51 protected final Filter<String> classFilter;
52
53 public UsageGraph(Filter<String> classFilter) {
54 this.classFilter = classFilter;
55 }
56
57 public Object accept(NodeVisitor visitor, Object data) {
58 for (ClassNode classNode : classNodes) {
59 visitor.visit(classNode, data);
60 }
61 return data;
62 }
63
64 public boolean isClass(String className) {
65 checkClassName(className);
66 return Collections.binarySearch(classNodes, className, ClassNodeComparator.INSTANCE) >= 0;
67 }
68
69 public ClassNode defineClass(String className) {
70 checkClassName(className);
71 int index = Collections.binarySearch(classNodes, className, ClassNodeComparator.INSTANCE);
72 ClassNode classNode;
73 if (index >= 0) {
74 classNode = classNodes.get(index);
75 } else {
76 classNode = new ClassNode(className);
77 classNodes.add(-(index + 1), classNode);
78 }
79 return classNode;
80 }
81
82 public FieldNode defineField(String className, String name, String desc) {
83 ClassNode classNode = defineClass(className);
84 return classNode.defineField(name, desc);
85 }
86
87 public MemberNode defineConstructor(String className, String name, String desc) {
88 ClassNode classNode = defineClass(className);
89 return classNode.defineConstructor(name, desc);
90 }
91
92 public MemberNode defineMethod(String className, String name, String desc) {
93 ClassNode classNode = defineClass(className);
94 if (ClassLoaderUtil.CLINIT.equals(name) || ClassLoaderUtil.INIT.equals(name)) {
95 return classNode.defineConstructor(name, desc);
96 } else {
97 return classNode.defineMethod(name, desc);
98 }
99 }
100
101 public void usageField(String className, String name, String desc, MemberNode usingMemberNode) {
102 checkClassName(className);
103 if (classFilter.filter(className)) {
104 FieldNode fieldNode = defineField(className, name, desc);
105 usage(fieldNode, usingMemberNode);
106 }
107 }
108
109 public void usageMethod(String className, String name, String desc, MemberNode usingMemberNode) {
110 checkClassName(className);
111 if (classFilter.filter(className)) {
112 MemberNode memberNode;
113 if (ClassLoaderUtil.CLINIT.equals(name) || ClassLoaderUtil.INIT.equals(name)) {
114 memberNode = defineConstructor(className, name, desc);
115 } else {
116 memberNode = defineMethod(className, name, desc);
117 }
118 usage(memberNode, usingMemberNode);
119 }
120 }
121
122 private void usage(MemberNode use, MemberNode user) {
123 use.addUser(user);
124 user.addUse(use);
125 }
126
127 private final void checkClassName(String className) {
128
129 if (className.indexOf("/") >= 0 || className.indexOf("\\") >= 0) {
130 throw new IllegalArgumentException("Invalid class name: " + className);
131 }
132 }
133 }