View Javadoc

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