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 }