001 /**
002 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003 */
004 package net.sourceforge.pmd.dcd.graph;
005
006 import java.util.ArrayList;
007 import java.util.Collections;
008 import java.util.List;
009
010 import net.sourceforge.pmd.dcd.ClassLoaderUtil;
011 import net.sourceforge.pmd.util.filter.Filter;
012
013 /**
014 * A UsageGraph tracks usage references between Java classes, based upon
015 * a parsing of the class files. Once the UsageGraph is built, it may be
016 * visited to perform additional post-processing, or usage relationship
017 * analysis.
018 * <p>
019 * The UsageGraph is composed of ClassNodes. Each ClassNode has various
020 * MemberNodes, specifically ConstructorNodes, FieldNodes, and MethodNodes.
021 * Each of these MemberNodes keeps track of other MemberNodes which it
022 * <em>uses</em> and other MemberNodes which are <em>users</em> of it. In
023 * this sense, the graph can navigated bi-directionally across the <em>use</em>
024 * relationship between MemberNodes.
025 * <p>
026 * Great effort is taken to keep the bookkeeping of the UsageGraph as tight
027 * as possible, so that rather large code bases can be analyzed. While nodes
028 * can grant access to the underlying Java Reflection APIs (e.g. Class,
029 * Constructor, Field, Member), the results are stored using WeakReferences
030 * to assist with memory usage.
031 * <p>
032 * A class Filter can be specified to limit the set of classes on which
033 * usage references will be tracked. This is often done to limit memory
034 * usage to interesting classes. For example, the <code>java.util</code>
035 * package is very often used, and tracking usages would require a massive
036 * bookkeeping effort which has little value.
037 *
038 * @see UsageGraphBuilder
039 * @see ClassNode
040 * @see MemberNode
041 * @see ConstructorNode
042 * @see FieldNode
043 * @see MethodNode
044 * @see NodeVisitor
045 * @see NodeVisitorAcceptor
046 */
047 public class UsageGraph implements NodeVisitorAcceptor {
048
049 private final List<ClassNode> classNodes = new ArrayList<ClassNode>();
050
051 protected final Filter<String> classFilter;
052
053 public UsageGraph(Filter<String> classFilter) {
054 this.classFilter = classFilter;
055 }
056
057 public Object accept(NodeVisitor visitor, Object data) {
058 for (ClassNode classNode : classNodes) {
059 visitor.visit(classNode, data);
060 }
061 return data;
062 }
063
064 public boolean isClass(String className) {
065 checkClassName(className);
066 return Collections.binarySearch(classNodes, className, ClassNodeComparator.INSTANCE) >= 0;
067 }
068
069 public ClassNode defineClass(String className) {
070 checkClassName(className);
071 int index = Collections.binarySearch(classNodes, className, ClassNodeComparator.INSTANCE);
072 ClassNode classNode;
073 if (index >= 0) {
074 classNode = classNodes.get(index);
075 } else {
076 classNode = new ClassNode(className);
077 classNodes.add(-(index + 1), classNode);
078 }
079 return classNode;
080 }
081
082 public FieldNode defineField(String className, String name, String desc) {
083 ClassNode classNode = defineClass(className);
084 return classNode.defineField(name, desc);
085 }
086
087 public MemberNode defineConstructor(String className, String name, String desc) {
088 ClassNode classNode = defineClass(className);
089 return classNode.defineConstructor(name, desc);
090 }
091
092 public MemberNode defineMethod(String className, String name, String desc) {
093 ClassNode classNode = defineClass(className);
094 if (ClassLoaderUtil.CLINIT.equals(name) || ClassLoaderUtil.INIT.equals(name)) {
095 return classNode.defineConstructor(name, desc);
096 } else {
097 return classNode.defineMethod(name, desc);
098 }
099 }
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 }
|