1 package net.sourceforge.pmd;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.Iterator;
7 import java.util.LinkedHashMap;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11
12 import net.sourceforge.pmd.ast.CompilationUnit;
13 import net.sourceforge.pmd.ast.SimpleNode;
14 import net.sourceforge.pmd.util.Benchmark;
15
16 /**
17 * This is a base class for RuleChainVisitor implementations which
18 * extracts interesting nodes from an AST, and lets each Rule visit
19 * the nodes it has expressed interest in.
20 */
21 public abstract class AbstractRuleChainVisitor implements RuleChainVisitor {
22 /**
23 * These are all the rules participating in the RuleChain, grouped by RuleSet.
24 */
25 protected Map<RuleSet, List<Rule>> ruleSetRules = new LinkedHashMap<RuleSet, List<Rule>>();
26
27 /**
28 * This is a mapping from node names to nodes instances for the current AST.
29 */
30 protected Map<String, List<SimpleNode>> nodeNameToNodes;
31
32 /**
33 * @see RuleChainVisitor#add(RuleSet, Rule)
34 */
35 public void add(RuleSet ruleSet, Rule rule) {
36 if (!ruleSetRules.containsKey(ruleSet)) {
37 ruleSetRules.put(ruleSet, new ArrayList<Rule>());
38 }
39 ruleSetRules.get(ruleSet).add(rule);
40 }
41
42 /**
43 * @see RuleChainVisitor#visitAll(List, RuleContext)
44 */
45 public void visitAll(List<CompilationUnit> astCompilationUnits, RuleContext ctx) {
46 initialize();
47 clear();
48
49
50
51 long start = System.nanoTime();
52 indexNodes(astCompilationUnits, ctx);
53 long end = System.nanoTime();
54 Benchmark.mark(Benchmark.TYPE_RULE_CHAIN_VISIT, end - start, 1);
55
56
57 for (RuleSet ruleSet : ruleSetRules.keySet()) {
58 if (!ruleSet.applies(ctx.getSourceCodeFile())) {
59 continue;
60 }
61
62 int visits = 0;
63 start = System.nanoTime();
64 for (Rule rule: ruleSetRules.get(ruleSet)) {
65 final List<String> nodeNames = rule.getRuleChainVisits();
66 for (int j = 0; j < nodeNames.size(); j++) {
67 List<SimpleNode> nodes = nodeNameToNodes.get(nodeNames.get(j));
68 for (SimpleNode node: nodes) {
69
70 while (rule instanceof RuleReference) {
71 rule = ((RuleReference)rule).getRule();
72 }
73 visit(rule, node, ctx);
74 }
75 visits += nodes.size();
76 }
77 end = System.nanoTime();
78 Benchmark.mark(Benchmark.TYPE_RULE_CHAIN_RULE, rule.getName(), end - start, visits);
79 start = end;
80 }
81 }
82 }
83
84 /**
85 * Visit the given rule to the given node.
86 */
87 protected abstract void visit(Rule rule, SimpleNode node, RuleContext ctx);
88
89 /**
90 * Index all nodes for visitation by rules.
91 */
92 protected abstract void indexNodes(List<CompilationUnit> astCompilationUnits, RuleContext ctx);
93
94 /**
95 * Index a single node for visitation by rules.
96 */
97 protected void indexNode(SimpleNode node) {
98 List<SimpleNode> nodes = nodeNameToNodes.get(node.toString());
99 if (nodes != null) {
100 nodes.add(node);
101 }
102 }
103
104 /**
105 * Initialize the RuleChainVisitor to be ready to perform visitations. This
106 * method should not be called until it is known that all Rules participating
107 * in the RuleChain are ready to be initialized themselves. Some rules
108 * may require full initialization to determine if they will participate in
109 * the RuleChain, so this has been delayed as long as possible to ensure
110 * that manipulation of the Rules is no longer occurring.
111 */
112 protected void initialize() {
113 if (nodeNameToNodes != null) {
114 return;
115 }
116
117
118 Set<String> visitedNodes = new HashSet<String>();
119 for (Iterator<Map.Entry<RuleSet, List<Rule>>> entryIterator = ruleSetRules.entrySet().iterator(); entryIterator.hasNext();) {
120 Map.Entry<RuleSet, List<Rule>> entry = entryIterator.next();
121 for (Iterator<Rule> ruleIterator = entry.getValue().iterator(); ruleIterator.hasNext();) {
122 Rule rule = ruleIterator.next();
123 if (rule.usesRuleChain()) {
124 visitedNodes.addAll(rule.getRuleChainVisits());
125 }
126 else {
127
128 ruleIterator.remove();
129 }
130 }
131
132 if (entry.getValue().isEmpty()) {
133 entryIterator.remove();
134 }
135 }
136
137
138
139
140 nodeNameToNodes = new HashMap<String, List<SimpleNode>>();
141 for (String s: visitedNodes) {
142 List<SimpleNode> nodes = new ArrayList<SimpleNode>(100);
143 nodeNameToNodes.put(s, nodes);
144 }
145 }
146
147 /**
148 * Clears the internal data structure used to manage the nodes visited
149 * between visiting different ASTs.
150 */
151 protected void clear() {
152 for (List<SimpleNode> l: nodeNameToNodes.values()) {
153 l.clear();
154 }
155 }
156 }