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