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 }