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              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                          // 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 }