View Javadoc

    1   package net.sourceforge.pmd.rules;
    2
    3   import java.util.HashMap;
    4   import java.util.Iterator;
    5   import java.util.List;
    6   import java.util.Map.Entry;
    7
    8   import net.sourceforge.pmd.AbstractRule;
    9   import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
    10  import net.sourceforge.pmd.ast.Node;
    11  import net.sourceforge.pmd.ast.SimpleNode;
    12  import net.sourceforge.pmd.jaxen.DocumentNavigator;
    13  import net.sourceforge.pmd.jaxen.MatchesFunction;
    14
    15  import org.jaxen.BaseXPath;
    16  import org.jaxen.JaxenException;
    17  import org.jaxen.SimpleVariableContext;
    18  import org.jaxen.XPath;
    19  import org.objectweb.asm.ClassWriter;
    20  import org.objectweb.asm.MethodVisitor;
    21  import org.objectweb.asm.Opcodes;
    22
    23  public class DynamicXPathRule extends AbstractRule implements Opcodes {
    24
    25      protected DynamicXPathRule() {
    26      }
    27
    28      private static HashMap classes = new HashMap();
    29
    30      public static synchronized Class loadClass(ClassLoader classloader, String type) {
    31          Class c = (Class) classes.get(type);
    32          if (c == null) {
    33              byte bytecode[] = buildClass(type);
    34              c = new ByteArrayClassLoader(classloader).loadClass(bytecode);
    35
    36              classes.put(type, c);
    37          }
    38
    39          return c;
    40      }
    41
    42      private static class ByteArrayClassLoader extends ClassLoader {
    43          ByteArrayClassLoader(ClassLoader parent) {
    44              super(parent);
    45          }
    46
    47          Class loadClass(byte[] data) {
    48              return defineClass(null, data, 0, data.length, null);
    49          }
    50      }
    51
    52      private static byte[] buildClass(String type) {
    53          String className = "net/sourceforge/pmd/rules/" + type + "XPathRule";
    54          String methodSig = "(Lnet/sourceforge/pmd/ast/AST" + type + ";Ljava/lang/Object;)Ljava/lang/Object;";
    55
    56          ClassWriter cw = new ClassWriter(0);
    57          MethodVisitor mv;
    58
    59          cw.visit(V1_4, ACC_PUBLIC + ACC_SUPER, className, null, "net/sourceforge/pmd/rules/DynamicXPathRule", null);
    60
    61          mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
    62          mv.visitCode();
    63          mv.visitVarInsn(ALOAD, 0);
    64          mv.visitMethodInsn(INVOKESPECIAL, "net/sourceforge/pmd/rules/DynamicXPathRule", "<init>", "()V");
    65          mv.visitInsn(RETURN);
    66          mv.visitMaxs(1, 1);
    67          mv.visitEnd();
    68
    69          mv = cw.visitMethod(ACC_PUBLIC, "visit", methodSig, null, null);
    70          mv.visitCode();
    71          mv.visitVarInsn(ALOAD, 0);
    72          mv.visitVarInsn(ALOAD, 1);
    73          mv.visitVarInsn(ALOAD, 2);
    74          mv.visitMethodInsn(INVOKEVIRTUAL, className, "evaluate", "(Lnet/sourceforge/pmd/ast/Node;Ljava/lang/Object;)V");
    75          mv.visitVarInsn(ALOAD, 0);
    76          mv.visitVarInsn(ALOAD, 1);
    77          mv.visitVarInsn(ALOAD, 2);
    78          mv.visitMethodInsn(INVOKESPECIAL, "net/sourceforge/pmd/rules/DynamicXPathRule", "visit", methodSig);
    79          mv.visitInsn(ARETURN);
    80          mv.visitMaxs(3, 3);
    81          mv.visitEnd();
    82
    83          cw.visitEnd();
    84
    85          return cw.toByteArray();
    86      }
    87
    88
    89      private XPath xpath;
    90
    91      private boolean regexpFunctionRegistered;
    92
    93      /***
    94       * Evaluate the AST with compilationUnit as root-node, against
    95       * the XPath expression found as property with name "xpath".
    96       * All matches are reported as violations.
    97       *
    98       * @param compilationUnit the Node that is the root of the AST to be checked
    99       * @param data
    100      */
    101     public void evaluate(Node compilationUnit, Object data) {
    102         try {
    103             initializeXPathExpression();
    104             List results = xpath.selectNodes(compilationUnit);
    105             for (Iterator i = results.iterator(); i.hasNext();) {
    106                 SimpleNode n = (SimpleNode) i.next();
    107                 if (n instanceof ASTVariableDeclaratorId && getBooleanProperty("pluginname")) {
    108                     addViolation(data, n, n.getImage());
    109                 } else {
    110                     addViolation(data, (SimpleNode) n, getMessage());
    111                 }
    112             }
    113         } catch (JaxenException ex) {
    114             throw new RuntimeException(ex);
    115         }
    116     }
    117
    118     private void initializeXPathExpression() throws JaxenException {
    119         if (xpath != null) {
    120             return;
    121         }
    122
    123         if (!regexpFunctionRegistered) {
    124             MatchesFunction.registerSelfInSimpleContext();
    125             regexpFunctionRegistered = true;
    126         }
    127
    128         String prop = getStringProperty("xpath");
    129
    130         String tail = prop.trim().replaceFirst("^////w+", "");
    131         String subquery = '.' + tail.trim();
    132
    133         xpath = new BaseXPath(subquery, new DocumentNavigator());
    134         if (properties.size() > 1) {
    135             SimpleVariableContext vc = new SimpleVariableContext();
    136             for (Iterator i = properties.entrySet().iterator(); i.hasNext();) {
    137                 Entry e = (Entry) i.next();
    138                 if (!"xpath".equals(e.getKey())) {
    139                     vc.setVariableValue((String) e.getKey(), e.getValue());
    140                 }
    141             }
    142             xpath.setVariableContext(vc);
    143         }
    144     }
    145
    146 }
    147