View Javadoc

    1   /*
    2    * Created on 14.07.2004
    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.NameOccurrence;
    24  import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
    25
    26  /**
    27   * @author raik, Sven Jacob
    28   *         <p/>
    29   *         Searches for special nodes and computes based on the sequence, the type of
    30   *         access of a variable.
    31   */
    32  public class VariableAccessVisitor extends JavaParserVisitorAdapter {
    33
    34      public void compute(ASTMethodDeclaration node) {
    35  	if (node.jjtGetParent() instanceof ASTClassOrInterfaceBodyDeclaration) {
    36  	    this.computeNow(node);
    37  	}
    38      }
    39
    40      public void compute(ASTConstructorDeclaration node) {
    41  	this.computeNow(node);
    42      }
    43
    44      private void computeNow(Node node) {
    45  	DataFlowNode inode = node.getDataFlowNode();
    46
    47  	List<VariableAccess> undefinitions = markUsages(inode);
    48
    49  	// all variables are first in state undefinition
    50  	DataFlowNode firstINode = inode.getFlow().get(0);
    51  	firstINode.setVariableAccess(undefinitions);
    52
    53  	// all variables are getting undefined when leaving scope
    54  	DataFlowNode lastINode = inode.getFlow().get(inode.getFlow().size() - 1);
    55  	lastINode.setVariableAccess(undefinitions);
    56      }
    57
    58      private List<VariableAccess> markUsages(DataFlowNode inode) {
    59  	// undefinitions was once a field... seems like it works fine as a local
    60  	List<VariableAccess> undefinitions = new ArrayList<VariableAccess>();
    61  	Set<Map<VariableNameDeclaration, List<NameOccurrence>>> variableDeclarations = collectDeclarations(inode);
    62  	for (Map<VariableNameDeclaration, List<NameOccurrence>> declarations : variableDeclarations) {
    63  	    for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry : declarations.entrySet()) {
    64  		VariableNameDeclaration vnd = entry.getKey();
    65
    66  		if (vnd.getAccessNodeParent() instanceof ASTFormalParameter) {
    67  		    // no definition/undefinition/references for parameters
    68  		    continue;
    69  		} else if (((Node) vnd.getAccessNodeParent()).getFirstDescendantOfType(ASTVariableInitializer.class) != null) {
    70  		    // add definition for initialized variables
    71  		    addVariableAccess(vnd.getNode(), new VariableAccess(VariableAccess.DEFINITION, vnd.getImage()),
    72  			    inode.getFlow());
    73  		}
    74  		undefinitions.add(new VariableAccess(VariableAccess.UNDEFINITION, vnd.getImage()));
    75
    76  		for (NameOccurrence occurrence : entry.getValue()) {
    77  		    addAccess(occurrence, inode);
    78  		}
    79  	    }
    80  	}
    81  	return undefinitions;
    82      }
    83
    84      private Set<Map<VariableNameDeclaration, List<NameOccurrence>>> collectDeclarations(DataFlowNode inode) {
    85  	Set<Map<VariableNameDeclaration, List<NameOccurrence>>> decls = new HashSet<Map<VariableNameDeclaration, List<NameOccurrence>>>();
    86  	Map<VariableNameDeclaration, List<NameOccurrence>> varDecls;
    87  	for (int i = 0; i < inode.getFlow().size(); i++) {
    88  	    DataFlowNode n = inode.getFlow().get(i);
    89  	    if (n instanceof StartOrEndDataFlowNode) {
    90  		continue;
    91  	    }
    92  	    varDecls = ((JavaNode)n.getNode()).getScope().getVariableDeclarations();
    93  	    if (!decls.contains(varDecls)) {
    94  		decls.add(varDecls);
    95  	    }
    96  	}
    97  	return decls;
    98      }
    99
    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 }