001: package net.sourceforge.pmd;
002:
003: import java.util.ArrayList;
004: import java.util.HashMap;
005: import java.util.HashSet;
006: import java.util.Iterator;
007: import java.util.List;
008: import java.util.Map;
009: import java.util.Set;
010:
011: import net.sourceforge.pmd.ast.CompilationUnit;
012: import net.sourceforge.pmd.ast.SimpleNode;
013: import net.sourceforge.pmd.util.Benchmark;
014:
015: /**
016: * This is a base class for RuleChainVisitor implementations which
017: * extracts interesting nodes from an AST, and lets each Rule visit
018: * the nodes it has expressed interest in.
019: */
020: public abstract class AbstractRuleChainVisitor implements
021: RuleChainVisitor {
022: /**
023: * These are all the rules participating in the RuleChain.
024: */
025: protected List<Rule> rules = new ArrayList<Rule>();
026:
027: /**
028: * This is a mapping from node names to nodes instances for the current AST.
029: */
030: protected Map<String, List<SimpleNode>> nodeNameToNodes;
031:
032: /**
033: * @see RuleChainVisitor#add(Rule)
034: */
035: public void add(Rule rule) {
036: rules.add(rule);
037: }
038:
039: /**
040: * @see RuleChainVisitor#visitAll(List, RuleContext)
041: */
042: public void visitAll(List<CompilationUnit> astCompilationUnits,
043: RuleContext ctx) {
044: initialize();
045: clear();
046:
047: // Perform a visitation of the AST to index nodes which need visiting by
048: // type
049: long start = System.nanoTime();
050: indexNodes(astCompilationUnits, ctx);
051: long end = System.nanoTime();
052: Benchmark.mark(Benchmark.TYPE_RULE_CHAIN_VISIT, end - start, 1);
053:
054: // For each rule, allow it to visit the nodes it desires
055: int visits = 0;
056: start = System.nanoTime();
057: for (Rule rule : rules) {
058: final List<String> nodeNames = rule.getRuleChainVisits();
059: for (int j = 0; j < nodeNames.size(); j++) {
060: List<SimpleNode> nodes = nodeNameToNodes.get(nodeNames
061: .get(j));
062: for (SimpleNode node : nodes) {
063: // Visit with underlying Rule, not the RuleReference
064: if (rule instanceof RuleReference) {
065: rule = ((RuleReference) rule).getRule();
066: }
067: visit(rule, node, ctx);
068: }
069: visits += nodes.size();
070: }
071: end = System.nanoTime();
072: Benchmark.mark(Benchmark.TYPE_RULE_CHAIN_RULE, rule
073: .getName(), end - start, visits);
074: start = end;
075: }
076: }
077:
078: /**
079: * Visit the given rule to the given node.
080: */
081: protected abstract void visit(Rule rule, SimpleNode node,
082: RuleContext ctx);
083:
084: /**
085: * Index all nodes for visitation by rules.
086: */
087: protected abstract void indexNodes(
088: List<CompilationUnit> astCompilationUnits, RuleContext ctx);
089:
090: /**
091: * Index a single node for visitation by rules.
092: */
093: protected void indexNode(SimpleNode node) {
094: List<SimpleNode> nodes = nodeNameToNodes.get(node.toString());
095: if (nodes != null) {
096: nodes.add(node);
097: }
098: }
099:
100: /**
101: * Initialize the RuleChainVisitor to be ready to perform visitations. This
102: * method should not be called until it is known that all Rules participating
103: * in the RuleChain are ready to be initialized themselves. Some rules
104: * may require full initialization to determine if they will participate in
105: * the RuleChain, so this has been delayed as long as possible to ensure
106: * that manipulation of the Rules is no longer occurring.
107: */
108: protected void initialize() {
109: if (nodeNameToNodes != null) {
110: return;
111: }
112:
113: // Determine all node types that need visiting
114: Set<String> visitedNodes = new HashSet<String>();
115: for (Iterator<Rule> i = rules.iterator(); i.hasNext();) {
116: Rule rule = i.next();
117: if (rule.usesRuleChain()) {
118: visitedNodes.addAll(rule.getRuleChainVisits());
119: } else {
120: // Drop rules which do not participate in the rule chain.
121: i.remove();
122: }
123: }
124:
125: // Setup the data structure to manage mapping node names to node
126: // instances. We intend to reuse this data structure between
127: // visits to different ASTs.
128: nodeNameToNodes = new HashMap<String, List<SimpleNode>>();
129: for (String s : visitedNodes) {
130: List<SimpleNode> nodes = new ArrayList<SimpleNode>(100);
131: nodeNameToNodes.put(s, nodes);
132: }
133: }
134:
135: /**
136: * Clears the internal data structure used to manage the nodes visited
137: * between visiting different ASTs.
138: */
139: protected void clear() {
140: for (List<SimpleNode> l : nodeNameToNodes.values()) {
141: l.clear();
142: }
143: }
144: }
|