/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.xml.ast; import static net.sourceforge.pmd.lang.xml.ast.XmlNode.BEGIN_COLUMN; import static net.sourceforge.pmd.lang.xml.ast.XmlNode.BEGIN_LINE; import static net.sourceforge.pmd.lang.xml.ast.XmlNode.END_COLUMN; import static net.sourceforge.pmd.lang.xml.ast.XmlNode.END_LINE; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import net.sourceforge.pmd.lang.ast.xpath.Attribute; import net.sourceforge.pmd.util.CompoundIterator; public class XmlNodeInvocationHandler implements InvocationHandler { private final Node node; private Object userData; private XmlParser parser; public XmlNodeInvocationHandler(XmlParser parser, Node node) { this.parser = parser; this.node = node; } public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { // XmlNode method? if (method.getDeclaringClass().isAssignableFrom(XmlNode.class) && !"java.lang.Object".equals(method.getDeclaringClass().getName())) { if ("jjtGetNumChildren".equals(method.getName())) { return node.hasChildNodes() ? node.getChildNodes().getLength() : 0; } else if ("jjtGetChild".equals(method.getName())) { return parser.createProxy(node.getChildNodes().item(((Integer) args[0]).intValue())); } else if ("jjtGetChildIndex".equals(method.getName())) { Node parent = node.getParentNode(); NodeList childNodes = parent.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { if (node == childNodes.item(i)) { return i; } } throw new IllegalStateException("This node is not a child of its parent: " + node); } else if ("getImage".equals(method.getName())) { if (node instanceof Text) { return ((Text) node).getData(); } else { return null; } } else if ("jjtGetParent".equals(method.getName())) { Node parent = node.getParentNode(); if (parent != null && !(parent instanceof Document)) { return parser.createProxy(parent); } else { return null; } } else if ("getAttributeIterator".equals(method.getName())) { List<Iterator<Attribute>> iterators = new ArrayList<>(); // Expose DOM Attributes final NamedNodeMap attributes = node.getAttributes(); iterators.add(new Iterator<Attribute>() { private int index; public boolean hasNext() { return attributes != null && index < attributes.getLength(); } public Attribute next() { Node attributeNode = attributes.item(index++); return new Attribute(parser.createProxy(node), attributeNode.getNodeName(), attributeNode.getNodeValue()); } public void remove() { throw new UnsupportedOperationException(); } }); // Expose Text/CDATA nodes to have an 'Image' attribute like // AST Nodes if (proxy instanceof Text) { iterators.add(Collections.singletonList( new Attribute((net.sourceforge.pmd.lang.ast.Node) proxy, "Image", ((Text) proxy).getData())) .iterator()); } // Expose Java Attributes // iterators.add(new // AttributeAxisIterator((net.sourceforge.pmd.lang.ast.Node) // p)); return new CompoundIterator<Attribute>(iterators.toArray(new Iterator[iterators.size()])); } else if ("getBeginLine".equals(method.getName())) { return getUserData(BEGIN_LINE); } else if ("getBeginColumn".equals(method.getName())) { return getUserData(BEGIN_COLUMN); } else if ("getEndLine".equals(method.getName())) { return getUserData(END_LINE); } else if ("getEndColumn".equals(method.getName())) { return getUserData(END_COLUMN); } else if ("getNode".equals(method.getName())) { return node; } else if ("getUserData".equals(method.getName())) { return userData; } else if ("setUserData".equals(method.getName())) { userData = args[0]; return null; } else if ("isFindBoundary".equals(method.getName())) { return false; } throw new UnsupportedOperationException("Method not supported for XmlNode: " + method); } else { if ("toString".equals(method.getName())) { String s = node.getNodeName(); s = s.replace("#", ""); return s; } // Delegate method Object result = method.invoke(node, args); return result; } } private Integer getUserData(String key) { if (node.getUserData(key) != null) { return (Integer) node.getUserData(key); } return Integer.valueOf(-1); } }