001: package net.sourceforge.pmd.dcd.graph;
002:
003: import java.util.ArrayList;
004: import java.util.Collections;
005: import java.util.List;
006:
007: import net.sourceforge.pmd.dcd.ClassLoaderUtil;
008: import net.sourceforge.pmd.util.filter.Filter;
009:
010: /**
011: * A UsageGraph tracks usage references between Java classes, based upon
012: * a parsing of the class files. Once the UsageGraph is built, it may be
013: * visited to perform additional post-processing, or usage relationship
014: * analysis.
015: * <p>
016: * The UsageGraph is composed of ClassNodes. Each ClassNode has various
017: * MemberNodes, specifically ConstructorNodes, FieldNodes, and MethodNodes.
018: * Each of these MemberNodes keeps track of other MemberNodes which it
019: * <em>uses</em> and other MemberNodes which are <em>users</em> of it. In
020: * this sense, the graph can navigated bi-directionally across the <em>use</em>
021: * relationship between MemberNodes.
022: * <p>
023: * Great effort is taken to keep the bookkeeping of the UsageGraph as tight
024: * as possible, so that rather large code bases can be analyzed. While nodes
025: * can grant access to the underlying Java Reflection APIs (e.g. Class,
026: * Constructor, Field, Member), the results are stored using WeakReferences
027: * to assist with memory usage.
028: * <p>
029: * A class Filter can be specified to limit the set of classes on which
030: * usage references will be tracked. This is often done to limit memory
031: * usage to interesting classes. For example, the <code>java.util</code>
032: * package is very often used, and tracking usages would require a massive
033: * bookkeeping effort which has little value.
034: *
035: * @see UsageGraphBuilder
036: * @see ClassNode
037: * @see MemberNode
038: * @see ConstructorNode
039: * @see FieldNode
040: * @see MethodNode
041: * @see NodeVisitor
042: * @see NodeVisitorAcceptor
043: */
044: public class UsageGraph implements NodeVisitorAcceptor {
045:
046: private final List<ClassNode> classNodes = new ArrayList<ClassNode>();
047:
048: protected final Filter<String> classFilter;
049:
050: public UsageGraph(Filter<String> classFilter) {
051: this .classFilter = classFilter;
052: }
053:
054: public Object accept(NodeVisitor visitor, Object data) {
055: for (ClassNode classNode : classNodes) {
056: visitor.visit(classNode, data);
057: }
058: return data;
059: }
060:
061: public boolean isClass(String className) {
062: checkClassName(className);
063: return Collections.binarySearch(classNodes, className,
064: ClassNodeComparator.INSTANCE) >= 0;
065: }
066:
067: public ClassNode defineClass(String className) {
068: checkClassName(className);
069: int index = Collections.binarySearch(classNodes, className,
070: ClassNodeComparator.INSTANCE);
071: ClassNode classNode;
072: if (index >= 0) {
073: classNode = classNodes.get(index);
074: } else {
075: classNode = new ClassNode(className);
076: classNodes.add(-(index + 1), classNode);
077: }
078: return classNode;
079: }
080:
081: public FieldNode defineField(String className, String name,
082: String desc) {
083: ClassNode classNode = defineClass(className);
084: return classNode.defineField(name, desc);
085: }
086:
087: public MemberNode defineConstructor(String className, String name,
088: String desc) {
089: ClassNode classNode = defineClass(className);
090: return classNode.defineConstructor(name, desc);
091: }
092:
093: public MemberNode defineMethod(String className, String name,
094: String desc) {
095: ClassNode classNode = defineClass(className);
096: if (ClassLoaderUtil.CLINIT.equals(name)
097: || ClassLoaderUtil.INIT.equals(name)) {
098: return classNode.defineConstructor(name, desc);
099: } else {
100: return classNode.defineMethod(name, desc);
101: }
102: }
103:
104: public void usageField(String className, String name, String desc,
105: MemberNode usingMemberNode) {
106: checkClassName(className);
107: if (classFilter.filter(className)) {
108: FieldNode fieldNode = defineField(className, name, desc);
109: usage(fieldNode, usingMemberNode);
110: }
111: }
112:
113: public void usageMethod(String className, String name, String desc,
114: MemberNode usingMemberNode) {
115: checkClassName(className);
116: if (classFilter.filter(className)) {
117: MemberNode memberNode;
118: if (ClassLoaderUtil.CLINIT.equals(name)
119: || ClassLoaderUtil.INIT.equals(name)) {
120: memberNode = defineConstructor(className, name, desc);
121: } else {
122: memberNode = defineMethod(className, name, desc);
123: }
124: usage(memberNode, usingMemberNode);
125: }
126: }
127:
128: private void usage(MemberNode use, MemberNode user) {
129: use.addUser(user);
130: user.addUse(use);
131: }
132:
133: private final void checkClassName(String className) {
134: // Make sure it's not in byte code internal format, or file system path.
135: if (className.indexOf("/") >= 0 || className.indexOf("\\") >= 0) {
136: throw new IllegalArgumentException("Invalid class name: "
137: + className);
138: }
139: }
140: }
|