View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.xml.ast;
5   
6   import static net.sourceforge.pmd.lang.xml.ast.XmlNode.BEGIN_COLUMN;
7   import static net.sourceforge.pmd.lang.xml.ast.XmlNode.BEGIN_LINE;
8   import static net.sourceforge.pmd.lang.xml.ast.XmlNode.END_COLUMN;
9   import static net.sourceforge.pmd.lang.xml.ast.XmlNode.END_LINE;
10  
11  import java.lang.reflect.InvocationHandler;
12  import java.lang.reflect.Method;
13  import java.util.ArrayList;
14  import java.util.Collections;
15  import java.util.Iterator;
16  import java.util.List;
17  
18  import net.sourceforge.pmd.lang.ast.xpath.Attribute;
19  import net.sourceforge.pmd.util.CompoundIterator;
20  
21  import org.w3c.dom.Document;
22  import org.w3c.dom.NamedNodeMap;
23  import org.w3c.dom.Node;
24  import org.w3c.dom.NodeList;
25  import org.w3c.dom.Text;
26  
27  public class XmlNodeInvocationHandler implements InvocationHandler {
28      private final Node node;
29      private Object userData;
30      private XmlParser parser;
31  
32      public XmlNodeInvocationHandler(XmlParser parser, Node node) {
33          this.parser = parser;
34          this.node = node;
35      }
36  
37      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
38          // XmlNode method?
39          if (method.getDeclaringClass().isAssignableFrom(XmlNode.class)
40                  && !"java.lang.Object".equals(method.getDeclaringClass().getName())) {
41              if ("jjtGetNumChildren".equals(method.getName())) {
42                  return node.hasChildNodes() ? node.getChildNodes().getLength() : 0;
43              } else if ("jjtGetChild".equals(method.getName())) {
44                  return parser.createProxy(node.getChildNodes().item(((Integer) args[0]).intValue()));
45              } else if ("jjtGetChildIndex".equals(method.getName())) {
46                  Node parent = node.getParentNode();
47                  NodeList childNodes = parent.getChildNodes();
48                  for (int i = 0; i < childNodes.getLength(); i++) {
49                      if (node == childNodes.item(i)) {
50                          return i;
51                      }
52                  }
53                  throw new IllegalStateException("This node is not a child of its parent: " + node);
54              } else if ("getImage".equals(method.getName())) {
55                  if (node instanceof Text) {
56                      return ((Text) node).getData();
57                  } else {
58                      return null;
59                  }
60              } else if ("jjtGetParent".equals(method.getName())) {
61                  Node parent = node.getParentNode();
62                  if (parent != null && !(parent instanceof Document)) {
63                      return parser.createProxy(parent);
64                  } else {
65                      return null;
66                  }
67              } else if ("getAttributeIterator".equals(method.getName())) {
68                  List<Iterator<Attribute>> iterators = new ArrayList<>();
69  
70                  // Expose DOM Attributes
71                  final NamedNodeMap attributes = node.getAttributes();
72                  iterators.add(new Iterator<Attribute>() {
73                      private int index;
74  
75                      public boolean hasNext() {
76                          return attributes != null && index < attributes.getLength();
77                      }
78  
79                      public Attribute next() {
80                          Node attributeNode = attributes.item(index++);
81                          return new Attribute(parser.createProxy(node), attributeNode.getNodeName(), attributeNode
82                                  .getNodeValue());
83                      }
84  
85                      public void remove() {
86                          throw new UnsupportedOperationException();
87                      }
88                  });
89  
90                  // Expose Text/CDATA nodes to have an 'Image' attribute like
91                  // AST Nodes
92                  if (proxy instanceof Text) {
93                      iterators.add(Collections.singletonList(
94                              new Attribute((net.sourceforge.pmd.lang.ast.Node) proxy, "Image", ((Text) proxy)
95                                      .getData())).iterator());
96                  }
97  
98                  // Expose Java Attributes
99                  // iterators.add(new
100                 // AttributeAxisIterator((net.sourceforge.pmd.lang.ast.Node)
101                 // p));
102 
103                 return new CompoundIterator<Attribute>(iterators.toArray(new Iterator[iterators.size()]));
104             } else if ("getBeginLine".equals(method.getName())) {
105                 return getUserData(BEGIN_LINE);
106             } else if ("getBeginColumn".equals(method.getName())) {
107                 return getUserData(BEGIN_COLUMN);
108             } else if ("getEndLine".equals(method.getName())) {
109                 return getUserData(END_LINE);
110             } else if ("getEndColumn".equals(method.getName())) {
111                 return getUserData(END_COLUMN);
112             } else if ("getNode".equals(method.getName())) {
113                 return node;
114             } else if ("getUserData".equals(method.getName())) {
115                 return userData;
116             } else if ("setUserData".equals(method.getName())) {
117                 userData = args[0];
118                 return null;
119             } else if ("isFindBoundary".equals(method.getName())) {
120                 return false;
121             }
122             throw new UnsupportedOperationException("Method not supported for XmlNode: " + method);
123         }
124         // Delegate method
125         else {
126             if ("toString".equals(method.getName())) {
127                 String s = node.getNodeName();
128                 s = s.replace("#", "");
129                 return s;
130             }
131             Object result = method.invoke(node, args);
132             return result;
133         }
134     }
135 
136     private Integer getUserData(String key) {
137         if (node.getUserData(key) != null) {
138             return (Integer) node.getUserData(key);
139         }
140         return Integer.valueOf(-1);
141     }
142 }