VariableAccessVisitor.java
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 (((Nodevnd.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 (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 }