UsageGraph.java
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('/'>= || className.indexOf('\\'>= 0) {
130       throw new IllegalArgumentException("Invalid class name: " + className);
131     }
132   }
133 }