1
2
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.Hashtable;
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 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 propertyDescriptorsByName = asFixedMap(
46 new PropertyDescriptor[] { maxPathDescriptor, maxViolationsDescriptor});
47
48 protected Map 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();
75
76 final IDataFlowNode node = (IDataFlowNode) 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
88 return;
89 }
90
91 final Hashtable hash = new Hashtable();
92
93 final Iterator pathIterator = path.iterator();
94 while (pathIterator.hasNext()) {
95
96 IDataFlowNode inode = (IDataFlowNode) pathIterator.next();
97 if (inode.getVariableAccess() != null) {
98
99 for (int g = 0; g < inode.getVariableAccess().size(); g++) {
100 final VariableAccess va = (VariableAccess) inode.getVariableAccess().get(g);
101
102
103 final Usage lastUsage = (Usage) hash.get(va.getVariableName());
104 if (lastUsage != null) {
105
106 checkVariableAccess(inode, va, lastUsage);
107 }
108
109 final Usage newUsage = new Usage(va.getAccessType(), inode);
110
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
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() ) {
131 addDaaViolation(rc, lastNode, "DD", va.getVariableName(), startLine, endLine);
132 } else if (u.accessType == VariableAccess.UNDEFINITION && va.isReference()) {
133 addDaaViolation(rc, lastNode, "UR", va.getVariableName(), startLine, endLine);
134 } else if (u.accessType == VariableAccess.DEFINITION && va.isUndefinition()) {
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 final Object[] params = new Object[] { type, var, new Integer(startLine), new Integer(endLine) };
152 String msg = type;
153 if (getMessage() != null) {
154 msg = MessageFormat.format(getMessage(), params);
155 }
156 final DaaRuleViolation violation = new DaaRuleViolation(this, ctx, node, type, msg, var, startLine, endLine);
157 ctx.getReport().addRuleViolation(violation);
158 this.daaRuleViolations.add(violation);
159 this.currentRuleViolationCount++;
160 }
161 }
162
163 /***
164 * Maximum number of violations was already reached?
165 * @return
166 */
167 private boolean maxNumberOfViolationsReached() {
168 return this.currentRuleViolationCount >= this.maxRuleViolations;
169 }
170
171 /***
172 * Checks if a violation already exists.
173 * This is needed because on the different paths same anomalies can occur.
174 * @param type
175 * @param var
176 * @param startLine
177 * @param endLine
178 * @return true if the violation already was added to the report
179 */
180 private boolean violationAlreadyExists(String type, String var, int startLine, int endLine) {
181 final Iterator violationIterator = this.daaRuleViolations.iterator();
182 while (violationIterator.hasNext()) {
183 final DaaRuleViolation violation = (DaaRuleViolation)violationIterator.next();
184 if ((violation.getBeginLine() == startLine)
185 && (violation.getEndLine() == endLine)
186 && violation.getType().equals(type)
187 && violation.getVariableName().equals(var)) {
188 return true;
189 }
190 }
191 return false;
192 }
193 }