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