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 java.io.IOException;
7   import java.io.Reader;
8   import java.io.StringReader;
9   import java.lang.reflect.Proxy;
10  import java.util.Arrays;
11  import java.util.HashMap;
12  import java.util.LinkedHashSet;
13  import java.util.Map;
14  import java.util.Set;
15  
16  import javax.xml.parsers.DocumentBuilder;
17  import javax.xml.parsers.DocumentBuilderFactory;
18  import javax.xml.parsers.ParserConfigurationException;
19  
20  import net.sourceforge.pmd.lang.ast.ParseException;
21  import net.sourceforge.pmd.lang.ast.RootNode;
22  import net.sourceforge.pmd.lang.xml.XmlParserOptions;
23  
24  import org.apache.commons.io.IOUtils;
25  import org.w3c.dom.Document;
26  import org.w3c.dom.Node;
27  import org.xml.sax.InputSource;
28  import org.xml.sax.SAXException;
29  
30  public class XmlParser {
31      protected final XmlParserOptions parserOptions;
32      protected Map<Node, XmlNode> nodeCache = new HashMap<>();
33  
34      public XmlParser(XmlParserOptions parserOptions) {
35          this.parserOptions = parserOptions;
36      }
37  
38      protected Document parseDocument(Reader reader) throws ParseException {
39          nodeCache.clear();
40          try {
41              String xmlData = IOUtils.toString(reader);
42  
43              DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
44              dbf.setNamespaceAware(parserOptions.isNamespaceAware());
45              dbf.setValidating(parserOptions.isValidating());
46              dbf.setIgnoringComments(parserOptions.isIgnoringComments());
47              dbf.setIgnoringElementContentWhitespace(parserOptions.isIgnoringElementContentWhitespace());
48              dbf.setExpandEntityReferences(parserOptions.isExpandEntityReferences());
49              dbf.setCoalescing(parserOptions.isCoalescing());
50              dbf.setXIncludeAware(parserOptions.isXincludeAware());
51              dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
52              dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
53              DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
54              documentBuilder.setEntityResolver(parserOptions.getEntityResolver());
55              Document document = documentBuilder.parse(new InputSource(new StringReader(xmlData)));
56              DOMLineNumbers lineNumbers = new DOMLineNumbers(document, xmlData);
57              lineNumbers.determine();
58              return document;
59          } catch (ParserConfigurationException e) {
60              throw new ParseException(e);
61          } catch (SAXException e) {
62              throw new ParseException(e);
63          } catch (IOException e) {
64              throw new ParseException(e);
65          }
66      }
67  
68      public XmlNode parse(Reader reader) {
69          Document document = parseDocument(reader);
70          return createProxy(document);
71      }
72  
73      public XmlNode createProxy(Node node) {
74          XmlNode proxy = nodeCache.get(node);
75          if (proxy != null) {
76              return proxy;
77          }
78  
79          // TODO Change Parser interface to take ClassLoader?
80          LinkedHashSet<Class<?>> interfaces = new LinkedHashSet<>();
81          interfaces.add(XmlNode.class);
82          if (node instanceof Document) {
83              interfaces.add(RootNode.class);
84          }
85          addAllInterfaces(interfaces, node.getClass());
86  
87          proxy = (XmlNode) Proxy.newProxyInstance(XmlParser.class.getClassLoader(),
88                  interfaces.toArray(new Class[interfaces.size()]), new XmlNodeInvocationHandler(this, node));
89          nodeCache.put(node, proxy);
90          return proxy;
91      }
92  
93      public void addAllInterfaces(Set<Class<?>> interfaces, Class<?> clazz) {
94          interfaces.addAll(Arrays.asList(clazz.getInterfaces()));
95          if (clazz.getSuperclass() != null) {
96              addAllInterfaces(interfaces, clazz.getSuperclass());
97          }
98      }
99  
100 }