View Javadoc

    1   /***
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.rules;
    5
    6   import net.sourceforge.pmd.CommonAbstractRule;
    7   import net.sourceforge.pmd.RuleContext;
    8   import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
    9   import net.sourceforge.pmd.ast.Node;
    10  import net.sourceforge.pmd.ast.SimpleNode;
    11  import net.sourceforge.pmd.jaxen.DocumentNavigator;
    12  import net.sourceforge.pmd.jaxen.MatchesFunction;
    13
    14  import org.jaxen.BaseXPath;
    15  import org.jaxen.JaxenException;
    16  import org.jaxen.SimpleVariableContext;
    17  import org.jaxen.XPath;
    18
    19  import java.io.PrintStream;
    20  import java.io.PrintWriter;
    21  import java.util.Iterator;
    22  import java.util.List;
    23  import java.util.Map.Entry;
    24
    25  /***
    26   * Rule that tries to match an XPath expression against a DOM
    27   * view of the AST of a "compilation unit".
    28   * <p/>
    29   * This rule needs a property "xpath".
    30   */
    31  public class XPathRule extends CommonAbstractRule {
    32
    33      public static Class loadClass(ClassLoader classloader, String xpath, String name) {
    34          if (xpath.indexOf('|') != -1) {
    35              //System.err.println(name + " not a dynamic rule: " + xpath.trim().replaceAll("\n", ""));
    36              return XPathRule.class;
    37          }
    38          String part = xpath.trim();
    39
    40          // Need to use DOTALL mode because of potential line terminators
    41          if (!part.matches("(?s)////w+//W.*")) {
    42              //System.err.println(name + " not a dynamic rule: " + xpath.trim().replaceAll("\n", ""));
    43              return XPathRule.class;
    44          }
    45
    46          String tail = part.replaceFirst("^////w+", "");
    47          String nodeName = part.substring(2, part.indexOf(tail));
    48
    49          return DynamicXPathRule.loadClass(classloader, nodeName);
    50      }
    51
    52      private XPath xpath;
    53      private boolean regexpFunctionRegistered;
    54
    55      /***
    56       * Evaluate the AST with compilationUnit as root-node, against
    57       * the XPath expression found as property with name "xpath".
    58       * All matches are reported as violations.
    59       *
    60       * @param compilationUnit the Node that is the root of the AST to be checked
    61       * @param data
    62       */
    63      public void evaluate(Node compilationUnit, RuleContext data) {
    64          try {
    65              initializeXPathExpression();
    66              List results = xpath.selectNodes(compilationUnit);
    67              for (Iterator i = results.iterator(); i.hasNext();) {
    68                  SimpleNode n = (SimpleNode) i.next();
    69                  if (n instanceof ASTVariableDeclaratorId && getBooleanProperty("pluginname")) {
    70                      addViolation(data, n, n.getImage());
    71                  } else {
    72                      addViolation(data, (SimpleNode) n, getMessage());
    73                  }
    74              }
    75          } catch (JaxenException ex) {
    76              throwJaxenAsRuntime(ex);
    77          }
    78      }
    79
    80      private void initializeXPathExpression() throws JaxenException {
    81          if (xpath != null) {
    82              return;
    83          }
    84
    85          if (!regexpFunctionRegistered) {
    86              MatchesFunction.registerSelfInSimpleContext();
    87              regexpFunctionRegistered = true;
    88          }
    89
    90          xpath = new BaseXPath(getStringProperty("xpath"), new DocumentNavigator());
    91          if (properties.size() > 1) {
    92              SimpleVariableContext vc = new SimpleVariableContext();
    93              for (Iterator i = properties.entrySet().iterator(); i.hasNext();) {
    94                  Entry e = (Entry) i.next();
    95                  if (!"xpath".equals(e.getKey())) {
    96                      vc.setVariableValue((String) e.getKey(), e.getValue());
    97                  }
    98              }
    99              xpath.setVariableContext(vc);
    100         }
    101     }
    102
    103     private static void throwJaxenAsRuntime(final JaxenException ex) {
    104         throw new RuntimeException() {
    105             public void printStackTrace() {
    106                 super.printStackTrace();
    107                 ex.printStackTrace();
    108             }
    109
    110             public void printStackTrace(PrintWriter writer) {
    111                 super.printStackTrace(writer);
    112                 ex.printStackTrace(writer);
    113             }
    114
    115             public void printStackTrace(PrintStream stream) {
    116                 super.printStackTrace(stream);
    117                 ex.printStackTrace(stream);
    118             }
    119
    120             public String getMessage() {
    121                 return super.getMessage() + ex.getMessage();
    122             }
    123         };
    124     }
    125
    126     /***
    127      * Apply the rule to all compilation units.
    128      */
    129     public void apply(List astCompilationUnits, RuleContext ctx) {
    130         for (Iterator i = astCompilationUnits.iterator(); i.hasNext();) {
    131             evaluate((Node) i.next(), ctx);
    132         }
    133     }
    134 }