/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.xml.ast;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import net.sourceforge.pmd.lang.ast.ParseException;
import net.sourceforge.pmd.lang.ast.RootNode;
import net.sourceforge.pmd.lang.xml.XmlParserOptions;
public class XmlParser {
protected final XmlParserOptions parserOptions;
protected Map<Node, XmlNode> nodeCache = new HashMap<>();
public XmlParser(XmlParserOptions parserOptions) {
this.parserOptions = parserOptions;
}
protected Document parseDocument(Reader reader) throws ParseException {
nodeCache.clear();
try {
String xmlData = IOUtils.toString(reader);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(parserOptions.isNamespaceAware());
dbf.setValidating(parserOptions.isValidating());
dbf.setIgnoringComments(parserOptions.isIgnoringComments());
dbf.setIgnoringElementContentWhitespace(parserOptions.isIgnoringElementContentWhitespace());
dbf.setExpandEntityReferences(parserOptions.isExpandEntityReferences());
dbf.setCoalescing(parserOptions.isCoalescing());
dbf.setXIncludeAware(parserOptions.isXincludeAware());
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
documentBuilder.setEntityResolver(parserOptions.getEntityResolver());
Document document = documentBuilder.parse(new InputSource(new StringReader(xmlData)));
DOMLineNumbers lineNumbers = new DOMLineNumbers(document, xmlData);
lineNumbers.determine();
return document;
} catch (ParserConfigurationException e) {
throw new ParseException(e);
} catch (SAXException e) {
throw new ParseException(e);
} catch (IOException e) {
throw new ParseException(e);
}
}
public XmlNode parse(Reader reader) {
Document document = parseDocument(reader);
return createProxy(document);
}
public XmlNode createProxy(Node node) {
XmlNode proxy = nodeCache.get(node);
if (proxy != null) {
return proxy;
}
// TODO Change Parser interface to take ClassLoader?
LinkedHashSet<Class<?>> interfaces = new LinkedHashSet<>();
interfaces.add(XmlNode.class);
if (node instanceof Document) {
interfaces.add(RootNode.class);
}
addAllInterfaces(interfaces, node.getClass());
proxy = (XmlNode) Proxy.newProxyInstance(XmlParser.class.getClassLoader(),
interfaces.toArray(new Class[interfaces.size()]), new XmlNodeInvocationHandler(this, node));
nodeCache.put(node, proxy);
return proxy;
}
public void addAllInterfaces(Set<Class<?>> interfaces, Class<?> clazz) {
interfaces.addAll(Arrays.asList(clazz.getInterfaces()));
if (clazz.getSuperclass() != null) {
addAllInterfaces(interfaces, clazz.getSuperclass());
}
}
}