001 /*
002 * Created on 14.07.2004
003 */
004 package net.sourceforge.pmd.lang.java.dfa;
005
006 import java.util.ArrayList;
007 import java.util.HashSet;
008 import java.util.List;
009 import java.util.Map;
010 import java.util.Set;
011
012 import net.sourceforge.pmd.lang.ast.Node;
013 import net.sourceforge.pmd.lang.dfa.DataFlowNode;
014 import net.sourceforge.pmd.lang.dfa.StartOrEndDataFlowNode;
015 import net.sourceforge.pmd.lang.dfa.VariableAccess;
016 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
017 import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
018 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
019 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
020 import net.sourceforge.pmd.lang.java.ast.ASTVariableInitializer;
021 import net.sourceforge.pmd.lang.java.ast.JavaNode;
022 import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorAdapter;
023 import net.sourceforge.pmd.lang.java.symboltable.NameOccurrence;
024 import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
025
026 /**
027 * @author raik, Sven Jacob
028 * <p/>
029 * Searches for special nodes and computes based on the sequence, the type of
030 * access of a variable.
031 */
032 public class VariableAccessVisitor extends JavaParserVisitorAdapter {
033
034 public void compute(ASTMethodDeclaration node) {
035 if (node.jjtGetParent() instanceof ASTClassOrInterfaceBodyDeclaration) {
036 this.computeNow(node);
037 }
038 }
039
040 public void compute(ASTConstructorDeclaration node) {
041 this.computeNow(node);
042 }
043
044 private void computeNow(Node node) {
045 DataFlowNode inode = node.getDataFlowNode();
046
047 List<VariableAccess> undefinitions = markUsages(inode);
048
049 // all variables are first in state undefinition
050 DataFlowNode firstINode = inode.getFlow().get(0);
051 firstINode.setVariableAccess(undefinitions);
052
053 // all variables are getting undefined when leaving scope
054 DataFlowNode lastINode = inode.getFlow().get(inode.getFlow().size() - 1);
055 lastINode.setVariableAccess(undefinitions);
056 }
057
058 private List<VariableAccess> markUsages(DataFlowNode inode) {
059 // undefinitions was once a field... seems like it works fine as a local
060 List<VariableAccess> undefinitions = new ArrayList<VariableAccess>();
061 Set<Map<VariableNameDeclaration, List<NameOccurrence>>> variableDeclarations = collectDeclarations(inode);
062 for (Map<VariableNameDeclaration, List<NameOccurrence>> declarations : variableDeclarations) {
063 for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry : declarations.entrySet()) {
064 VariableNameDeclaration vnd = entry.getKey();
065
066 if (vnd.getAccessNodeParent() instanceof ASTFormalParameter) {
067 // no definition/undefinition/references for parameters
068 continue;
069 } else if (((Node) vnd.getAccessNodeParent()).getFirstDescendantOfType(ASTVariableInitializer.class) != null) {
070 // add definition for initialized variables
071 addVariableAccess(vnd.getNode(), new VariableAccess(VariableAccess.DEFINITION, vnd.getImage()),
072 inode.getFlow());
073 }
074 undefinitions.add(new VariableAccess(VariableAccess.UNDEFINITION, vnd.getImage()));
075
076 for (NameOccurrence occurrence : entry.getValue()) {
077 addAccess(occurrence, inode);
078 }
079 }
080 }
081 return undefinitions;
082 }
083
084 private Set<Map<VariableNameDeclaration, List<NameOccurrence>>> collectDeclarations(DataFlowNode inode) {
085 Set<Map<VariableNameDeclaration, List<NameOccurrence>>> decls = new HashSet<Map<VariableNameDeclaration, List<NameOccurrence>>>();
086 Map<VariableNameDeclaration, List<NameOccurrence>> varDecls;
087 for (int i = 0; i < inode.getFlow().size(); i++) {
088 DataFlowNode n = inode.getFlow().get(i);
089 if (n instanceof StartOrEndDataFlowNode) {
090 continue;
091 }
092 varDecls = ((JavaNode)n.getNode()).getScope().getVariableDeclarations();
093 if (!decls.contains(varDecls)) {
094 decls.add(varDecls);
095 }
096 }
097 return decls;
098 }
099
100 private void addAccess(NameOccurrence occurrence, DataFlowNode inode) {
101 if (occurrence.isOnLeftHandSide()) {
102 this.addVariableAccess(occurrence.getLocation(), new VariableAccess(VariableAccess.DEFINITION, occurrence
103 .getImage()), inode.getFlow());
104 } else if (occurrence.isOnRightHandSide() || !occurrence.isOnLeftHandSide() && !occurrence.isOnRightHandSide()) {
105 this.addVariableAccess(occurrence.getLocation(), new VariableAccess(VariableAccess.REFERENCING, occurrence
106 .getImage()), inode.getFlow());
107 }
108 }
109
110 /**
111 * Adds a VariableAccess to a dataflow node.
112 * @param node location of the access of a variable
113 * @param va variable access to add
114 * @param flow dataflownodes that can contain the node.
115 */
116 private void addVariableAccess(Node node, VariableAccess va, List<DataFlowNode> flow) {
117 // backwards to find the right inode (not a method declaration)
118 for (int i = flow.size() - 1; i > 0; i--) {
119 DataFlowNode inode = flow.get(i);
120 if (inode.getNode() == null) {
121 continue;
122 }
123
124 List<? extends Node> children = inode.getNode().findDescendantsOfType(node.getClass());
125 for (Node n : children) {
126 if (node.equals(n)) {
127 List<VariableAccess> v = new ArrayList<VariableAccess>();
128 v.add(va);
129 inode.setVariableAccess(v);
130 return;
131 }
132 }
133 }
134 }
135
136 }
|