001: /*
002: * Created on 20.07.2004
003: */
004: package net.sourceforge.pmd.dfa;
005:
006: import net.sourceforge.pmd.AbstractRule;
007: import net.sourceforge.pmd.PropertyDescriptor;
008: import net.sourceforge.pmd.RuleContext;
009: import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
010: import net.sourceforge.pmd.ast.ASTMethodDeclaration;
011: import net.sourceforge.pmd.ast.SimpleNode;
012: import net.sourceforge.pmd.dfa.pathfinder.CurrentPath;
013: import net.sourceforge.pmd.dfa.pathfinder.DAAPathFinder;
014: import net.sourceforge.pmd.dfa.pathfinder.Executable;
015: import net.sourceforge.pmd.dfa.variableaccess.VariableAccess;
016: import net.sourceforge.pmd.properties.IntegerProperty;
017:
018: import java.text.MessageFormat;
019: import java.util.ArrayList;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024:
025: /**
026: * Starts path search for each method and runs code if found.
027: *
028: * @author raik
029: * @author Sven Jacob
030: */
031: public class DaaRule extends AbstractRule implements Executable {
032: private RuleContext rc;
033: private List<DaaRuleViolation> daaRuleViolations;
034: private int maxRuleViolations;
035: private int currentRuleViolationCount;
036:
037: private static final PropertyDescriptor maxPathDescriptor = new IntegerProperty(
038: "maxpaths", "Maximum number of paths per method", 5000,
039: 1.0f);
040:
041: private static final PropertyDescriptor maxViolationsDescriptor = new IntegerProperty(
042: "maxviolations", "Maximum number of anomalys per class",
043: 1000, 2.0f);
044:
045: private static final Map<String, PropertyDescriptor> propertyDescriptorsByName = asFixedMap(new PropertyDescriptor[] {
046: maxPathDescriptor, maxViolationsDescriptor });
047:
048: protected Map<String, PropertyDescriptor> propertiesByName() {
049: return propertyDescriptorsByName;
050: }
051:
052: private static class Usage {
053: public int accessType;
054: public IDataFlowNode node;
055:
056: public Usage(int accessType, IDataFlowNode node) {
057: this .accessType = accessType;
058: this .node = node;
059: }
060:
061: public String toString() {
062: return "accessType = " + accessType + ", line = "
063: + node.getLine();
064: }
065: }
066:
067: public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
068: this .maxRuleViolations = getIntProperty(maxViolationsDescriptor);
069: this .currentRuleViolationCount = 0;
070: return super .visit(node, data);
071: }
072:
073: public Object visit(ASTMethodDeclaration methodDeclaration,
074: Object data) {
075: this .rc = (RuleContext) data;
076: this .daaRuleViolations = new ArrayList<DaaRuleViolation>();
077:
078: final IDataFlowNode node = methodDeclaration.getDataFlowNode()
079: .getFlow().get(0);
080:
081: final DAAPathFinder pathFinder = new DAAPathFinder(node, this ,
082: getIntProperty(maxPathDescriptor));
083: pathFinder.run();
084:
085: super .visit(methodDeclaration, data);
086: return data;
087: }
088:
089: public void execute(CurrentPath path) {
090: if (maxNumberOfViolationsReached()) {
091: // dont execute this path if the limit is already reached
092: return;
093: }
094:
095: final Map<String, Usage> hash = new HashMap<String, Usage>();
096:
097: final Iterator<IDataFlowNode> pathIterator = path.iterator();
098: while (pathIterator.hasNext()) {
099: // iterate all nodes in this path
100: IDataFlowNode inode = pathIterator.next();
101: if (inode.getVariableAccess() != null) {
102: // iterate all variables of this node
103: for (int g = 0; g < inode.getVariableAccess().size(); g++) {
104: final VariableAccess va = inode.getVariableAccess()
105: .get(g);
106:
107: // get the last usage of the current variable
108: final Usage lastUsage = hash.get(va
109: .getVariableName());
110: if (lastUsage != null) {
111: // there was a usage to this variable before
112: checkVariableAccess(inode, va, lastUsage);
113: }
114:
115: final Usage newUsage = new Usage(
116: va.getAccessType(), inode);
117: // put the new usage for the variable
118: hash.put(va.getVariableName(), newUsage);
119: }
120: }
121: }
122: }
123:
124: /**
125: * @param inode
126: * @param va
127: * @param o
128: */
129: private void checkVariableAccess(IDataFlowNode inode,
130: VariableAccess va, final Usage u) {
131: // get the start and end line
132: final int startLine = u.node.getLine();
133: final int endLine = inode.getLine();
134:
135: final SimpleNode lastNode = inode.getSimpleNode();
136: final SimpleNode firstNode = u.node.getSimpleNode();
137:
138: if (va.accessTypeMatches(u.accessType) && va.isDefinition()) { // DD
139: addDaaViolation(rc, lastNode, "DD", va.getVariableName(),
140: startLine, endLine);
141: } else if (u.accessType == VariableAccess.UNDEFINITION
142: && va.isReference()) { // UR
143: addDaaViolation(rc, lastNode, "UR", va.getVariableName(),
144: startLine, endLine);
145: } else if (u.accessType == VariableAccess.DEFINITION
146: && va.isUndefinition()) { // DU
147: addDaaViolation(rc, firstNode, "DU", va.getVariableName(),
148: startLine, endLine);
149: }
150: }
151:
152: /**
153: * Adds a daa violation to the report.
154: *
155: * @param ctx the RuleContext
156: * @param node the node that produces the violation
157: * @param msg specific message to put in the report
158: */
159: private final void addDaaViolation(Object data, SimpleNode node,
160: String type, String var, int startLine, int endLine) {
161: if (!maxNumberOfViolationsReached()
162: && !violationAlreadyExists(type, var, startLine,
163: endLine) && node != null) {
164: final RuleContext ctx = (RuleContext) data;
165: String msg = type;
166: if (getMessage() != null) {
167: msg = MessageFormat.format(getMessage(), type, var,
168: startLine, endLine);
169: }
170: final DaaRuleViolation violation = new DaaRuleViolation(
171: this , ctx, node, type, msg, var, startLine, endLine);
172: ctx.getReport().addRuleViolation(violation);
173: this .daaRuleViolations.add(violation);
174: this .currentRuleViolationCount++;
175: }
176: }
177:
178: /**
179: * Maximum number of violations was already reached?
180: * @return
181: */
182: private boolean maxNumberOfViolationsReached() {
183: return this .currentRuleViolationCount >= this .maxRuleViolations;
184: }
185:
186: /**
187: * Checks if a violation already exists.
188: * This is needed because on the different paths same anomalies can occur.
189: * @param type
190: * @param var
191: * @param startLine
192: * @param endLine
193: * @return true if the violation already was added to the report
194: */
195: private boolean violationAlreadyExists(String type, String var,
196: int startLine, int endLine) {
197: for (DaaRuleViolation violation : this .daaRuleViolations) {
198: if ((violation.getBeginLine() == startLine)
199: && (violation.getEndLine() == endLine)
200: && violation.getType().equals(type)
201: && violation.getVariableName().equals(var)) {
202: return true;
203: }
204: }
205: return false;
206: }
207: }
|