View Javadoc

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 		// Make sure it's not in byte code internal format, or file system path.
129 		if (className.indexOf("/") >= 0 || className.indexOf("\\") >= 0) {
130 			throw new IllegalArgumentException("Invalid class name: " + className);
131 		}
132 	}
133 }