View Javadoc

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          // Perform a visitation of the AST to index nodes which need visiting by
50          // type
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          // For each RuleSet, only if this source file applies
57          for (RuleSet ruleSet : ruleSetRules.keySet()) {
58              if (!ruleSet.applies(ctx.getSourceCodeFile())) {
59          	continue;
60              }
61              // For each rule, allow it to visit the nodes it desires
62              start = System.nanoTime();
63              for (Rule rule: ruleSetRules.get(ruleSet)) {
64                  int visits = 0;
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                          // Visit with underlying Rule, not the RuleReference
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         // Determine all node types that need visiting
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                     // Drop rules which do not participate in the rule chain.
128                     ruleIterator.remove();
129                 }
130             }
131             // Drop RuleSets in which all Rules have been dropped.
132             if (entry.getValue().isEmpty()) {
133         	entryIterator.remove();
134             }
135         }
136 
137         // Setup the data structure to manage mapping node names to node
138         // instances.  We intend to reuse this data structure between
139         // visits to different ASTs.
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 }