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