View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.java.dfa;
5   
6   import java.util.ArrayList;
7   import java.util.HashSet;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import net.sourceforge.pmd.lang.ast.Node;
13  import net.sourceforge.pmd.lang.dfa.DataFlowNode;
14  import net.sourceforge.pmd.lang.dfa.StartOrEndDataFlowNode;
15  import net.sourceforge.pmd.lang.dfa.VariableAccess;
16  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
17  import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
18  import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
19  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
20  import net.sourceforge.pmd.lang.java.ast.ASTVariableInitializer;
21  import net.sourceforge.pmd.lang.java.ast.JavaNode;
22  import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorAdapter;
23  import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence;
24  import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
25  import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
26  import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
27  
28  /**
29   *         Searches for special nodes and computes based on the sequence, the type of
30   *         access of a variable.
31   * @since Created on 14.07.2004
32   * @author raik, Sven Jacob
33   */
34  public class VariableAccessVisitor extends JavaParserVisitorAdapter {
35  
36      public void compute(ASTMethodDeclaration node) {
37  	if (node.jjtGetParent() instanceof ASTClassOrInterfaceBodyDeclaration) {
38  	    this.computeNow(node);
39  	}
40      }
41  
42      public void compute(ASTConstructorDeclaration node) {
43  	this.computeNow(node);
44      }
45  
46      private void computeNow(Node node) {
47  	DataFlowNode inode = node.getDataFlowNode();
48  
49  	List<VariableAccess> undefinitions = markUsages(inode);
50  
51  	// all variables are first in state undefinition
52  	DataFlowNode firstINode = inode.getFlow().get(0);
53  	firstINode.setVariableAccess(undefinitions);
54  
55  	// all variables are getting undefined when leaving scope
56  	DataFlowNode lastINode = inode.getFlow().get(inode.getFlow().size() - 1);
57  	lastINode.setVariableAccess(undefinitions);
58      }
59  
60      private List<VariableAccess> markUsages(DataFlowNode inode) {
61  	// undefinitions was once a field... seems like it works fine as a local
62  	List<VariableAccess> undefinitions = new ArrayList<VariableAccess>();
63  	Set<Map<NameDeclaration, List<NameOccurrence>>> variableDeclarations = collectDeclarations(inode);
64  	for (Map<NameDeclaration, List<NameOccurrence>> declarations : variableDeclarations) {
65  	    for (Map.Entry<NameDeclaration, List<NameOccurrence>> entry : declarations.entrySet()) {
66          VariableNameDeclaration vnd = (VariableNameDeclaration)entry.getKey();
67  
68  		if (vnd.getAccessNodeParent() instanceof ASTFormalParameter) {
69  		    // no definition/undefinition/references for parameters
70  		    continue;
71  		} else if (vnd.getAccessNodeParent().getFirstDescendantOfType(ASTVariableInitializer.class) != null) {
72  		    // add definition for initialized variables
73  		    addVariableAccess(vnd.getNode(), new VariableAccess(VariableAccess.DEFINITION, vnd.getImage()),
74  			    inode.getFlow());
75  		}
76  		undefinitions.add(new VariableAccess(VariableAccess.UNDEFINITION, vnd.getImage()));
77  
78  		for (NameOccurrence occurrence : entry.getValue()) {
79  		    addAccess((JavaNameOccurrence)occurrence, inode);
80  		}
81  	    }
82  	}
83  	return undefinitions;
84      }
85  
86      private Set<Map<NameDeclaration, List<NameOccurrence>>> collectDeclarations(DataFlowNode inode) {
87  	Set<Map<NameDeclaration, List<NameOccurrence>>> decls = new HashSet<Map<NameDeclaration, List<NameOccurrence>>>();
88  	Map<NameDeclaration, List<NameOccurrence>> varDecls;
89  	for (int i = 0; i < inode.getFlow().size(); i++) {
90  	    DataFlowNode n = inode.getFlow().get(i);
91  	    if (n instanceof StartOrEndDataFlowNode) {
92  		continue;
93  	    }
94  	    varDecls = ((JavaNode)n.getNode()).getScope().getDeclarations();
95  	    if (!decls.contains(varDecls)) {
96  		decls.add(varDecls);
97  	    }
98  	}
99  	return decls;
100     }
101 
102     private void addAccess(JavaNameOccurrence occurrence, DataFlowNode inode) {
103 	if (occurrence.isOnLeftHandSide()) {
104 	    this.addVariableAccess(occurrence.getLocation(), new VariableAccess(VariableAccess.DEFINITION, occurrence
105 		    .getImage()), inode.getFlow());
106 	} else if (occurrence.isOnRightHandSide() || !occurrence.isOnLeftHandSide() && !occurrence.isOnRightHandSide()) {
107 	    this.addVariableAccess(occurrence.getLocation(), new VariableAccess(VariableAccess.REFERENCING, occurrence
108 		    .getImage()), inode.getFlow());
109 	}
110     }
111 
112     /**
113      * Adds a VariableAccess to a dataflow node.
114      * @param node location of the access of a variable
115      * @param va variable access to add
116      * @param flow dataflownodes that can contain the node.
117      */
118     private void addVariableAccess(Node node, VariableAccess va, List<DataFlowNode> flow) {
119 	// backwards to find the right inode (not a method declaration)
120 	for (int i = flow.size() - 1; i > 0; i--) {
121 	    DataFlowNode inode = flow.get(i);
122 	    if (inode.getNode() == null) {
123 		continue;
124 	    }
125 
126 	    List<? extends Node> children = inode.getNode().findDescendantsOfType(node.getClass());
127 	    for (Node n : children) {
128 		if (node.equals(n)) {
129 		    List<VariableAccess> v = new ArrayList<VariableAccess>();
130 		    v.add(va);
131 		    inode.setVariableAccess(v);
132 		    return;
133 		}
134 	    }
135 	}
136     }
137 
138 }