View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.dfa;
5   
6   import net.sourceforge.pmd.AbstractRule;
7   import net.sourceforge.pmd.PropertyDescriptor;
8   import net.sourceforge.pmd.RuleContext;
9   import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
10  import net.sourceforge.pmd.ast.ASTMethodDeclaration;
11  import net.sourceforge.pmd.ast.SimpleNode;
12  import net.sourceforge.pmd.dfa.pathfinder.CurrentPath;
13  import net.sourceforge.pmd.dfa.pathfinder.DAAPathFinder;
14  import net.sourceforge.pmd.dfa.pathfinder.Executable;
15  import net.sourceforge.pmd.dfa.variableaccess.VariableAccess;
16  import net.sourceforge.pmd.properties.IntegerProperty;
17  
18  import java.text.MessageFormat;
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  
25  /**
26   * Starts path search for each method and runs code if found.
27   *
28   * @author raik
29   * @author Sven Jacob
30   */
31  public class DaaRule extends AbstractRule implements Executable {
32      private RuleContext rc;
33      private List<DaaRuleViolation> daaRuleViolations;
34      private int maxRuleViolations;
35      private int currentRuleViolationCount;
36  
37      private static final PropertyDescriptor maxPathDescriptor = new IntegerProperty(
38              "maxpaths", "Maximum number of paths per method", 5000, 1.0f
39              );
40  
41      private static final PropertyDescriptor maxViolationsDescriptor = new IntegerProperty(
42              "maxviolations", "Maximum number of anomalys per class", 1000, 2.0f
43              );
44  
45      private static final Map<String, PropertyDescriptor> propertyDescriptorsByName = asFixedMap(
46              new PropertyDescriptor[] { maxPathDescriptor, maxViolationsDescriptor});
47  
48      protected Map<String, PropertyDescriptor> propertiesByName() {
49          return propertyDescriptorsByName;
50      }
51  
52      private static class Usage {
53          public int accessType;
54          public IDataFlowNode node;
55  
56          public Usage(int accessType, IDataFlowNode node) {
57              this.accessType = accessType;
58              this.node = node;
59          }
60  
61          public String toString() {
62              return "accessType = " + accessType + ", line = " + node.getLine();
63          }
64      }
65  
66      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
67          this.maxRuleViolations = getIntProperty(maxViolationsDescriptor);
68          this.currentRuleViolationCount = 0;
69          return super.visit(node, data);
70      }
71  
72      public Object visit(ASTMethodDeclaration methodDeclaration, Object data) {
73          this.rc = (RuleContext) data;
74          this.daaRuleViolations = new ArrayList<DaaRuleViolation>();
75  
76          final IDataFlowNode node = methodDeclaration.getDataFlowNode().getFlow().get(0);
77  
78          final DAAPathFinder pathFinder = new DAAPathFinder(node, this, getIntProperty(maxPathDescriptor));
79          pathFinder.run();
80  
81          super.visit(methodDeclaration, data);
82          return data;
83      }
84  
85      public void execute(CurrentPath path) {
86          if (maxNumberOfViolationsReached()) {
87              // dont execute this path if the limit is already reached
88              return;
89          }
90  
91          final Map<String, Usage> hash = new HashMap<String, Usage>();
92  
93          final Iterator<IDataFlowNode> pathIterator = path.iterator();
94          while (pathIterator.hasNext()) {
95              // iterate all nodes in this path
96              IDataFlowNode inode = pathIterator.next();
97              if (inode.getVariableAccess() != null) {
98                  // iterate all variables of this node
99                  for (int g = 0; g < inode.getVariableAccess().size(); g++) {
100                     final VariableAccess va = inode.getVariableAccess().get(g);
101 
102                     // get the last usage of the current variable
103                     final Usage lastUsage = hash.get(va.getVariableName());
104                     if (lastUsage != null) {
105                         // there was a usage to this variable before
106                         checkVariableAccess(inode, va, lastUsage);
107                     }
108 
109                     final Usage newUsage = new Usage(va.getAccessType(), inode);
110                     // put the new usage for the variable
111                     hash.put(va.getVariableName(), newUsage);
112                 }
113             }
114         }
115     }
116 
117     /**
118      * @param inode
119      * @param va
120      * @param o
121      */
122     private void checkVariableAccess(IDataFlowNode inode, VariableAccess va, final Usage u) {
123         // get the start and end line
124         final int startLine = u.node.getLine();
125         final int endLine = inode.getLine();
126 
127         final SimpleNode lastNode = inode.getSimpleNode();
128         final SimpleNode firstNode = u.node.getSimpleNode();
129 
130         if (va.accessTypeMatches(u.accessType) && va.isDefinition() ) { // DD
131             addDaaViolation(rc, lastNode, "DD", va.getVariableName(), startLine, endLine);
132         } else if (u.accessType == VariableAccess.UNDEFINITION && va.isReference()) { // UR
133             addDaaViolation(rc, lastNode, "UR", va.getVariableName(), startLine, endLine);
134         } else if (u.accessType == VariableAccess.DEFINITION && va.isUndefinition()) { // DU
135             addDaaViolation(rc, firstNode, "DU", va.getVariableName(), startLine, endLine);
136         }
137     }
138 
139     /**
140      * Adds a daa violation to the report.
141      *
142      * @param ctx  the RuleContext
143      * @param node the node that produces the violation
144      * @param msg  specific message to put in the report
145      */
146     private final void addDaaViolation(Object data, SimpleNode node, String type, String var, int startLine, int endLine) {
147         if (!maxNumberOfViolationsReached()
148                 && !violationAlreadyExists(type, var, startLine, endLine)
149                 && node != null) {
150             final RuleContext ctx = (RuleContext) data;
151             String msg = type;
152             if (getMessage() != null) {
153                 msg = MessageFormat.format(getMessage(), type, var, startLine, endLine);
154             }
155             final DaaRuleViolation violation = new DaaRuleViolation(this, ctx, node, type, msg, var, startLine, endLine);
156             ctx.getReport().addRuleViolation(violation);
157             this.daaRuleViolations.add(violation);
158             this.currentRuleViolationCount++;
159       }
160     }
161 
162     /**
163      * Maximum number of violations was already reached?
164      * @return
165      */
166     private boolean maxNumberOfViolationsReached() {
167         return this.currentRuleViolationCount >= this.maxRuleViolations;
168     }
169 
170     /**
171      * Checks if a violation already exists.
172      * This is needed because on the different paths same anomalies can occur.
173      * @param type
174      * @param var
175      * @param startLine
176      * @param endLine
177      * @return true if the violation already was added to the report
178      */
179     private boolean violationAlreadyExists(String type, String var, int startLine, int endLine) {
180         for(DaaRuleViolation violation: this.daaRuleViolations) {
181             if ((violation.getBeginLine() == startLine)
182                     && (violation.getEndLine() == endLine)
183                     && violation.getType().equals(type)
184                     && violation.getVariableName().equals(var)) {
185                 return true;
186             }
187         }
188         return false;
189     }
190 }