Webiyo

This page was generated using Webiyo. See its source code and unit tests.

org/webiyo/xml/Xml.java

package org.webiyo.xml;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.*;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;

public class Xml {

    private static final String STRING_SYSTEM_ID = "<xml from string>";

    private Xml() {
    }

    public static Document parse(InputSource inputSource) throws IOException {
        Document result;
        try {
            result = newXmlParser(false, null).parse(inputSource);
        } catch (SAXException e) {
            throw new XmlException(e.getMessage(), e);
        }
        return result;
    }

    public static Document parse(String xml) throws IOException {
        InputSource source = new InputSource(new StringReader(xml));
        source.setSystemId(STRING_SYSTEM_ID);
        Document result;
        try {
            result = newXmlParser(false, null).parse(source);
        } catch (SAXParseException e) {
            String message = makeMessageWithSnippet(e, xml);
            throw new XmlException(message, e);
        } catch (SAXException e) {
            throw new XmlException(e.getMessage(), e);
        }
        return result;
    }

    public static Document parseAndValidate(String xml, EntityResolver resolver) throws SAXException, IOException {
        InputSource source = new InputSource(new StringReader(xml));
        source.setSystemId(STRING_SYSTEM_ID);

        Document result;
        try {
            result = newXmlParser(true, resolver).parse(source);
        } catch (SAXParseException e) {
            String message = makeMessageWithSnippet(e, xml);
            throw new XmlException(message, e);
        }
        return result;
    }

    public static Element selectElement(Element itemElt, String xpath) throws XmlException {
        return selectOne(itemElt, xpath, Element.class);
    }

    public static <T> T selectOne(Element element, String xpath, Class<T> expectedNodeClass) throws XmlException {
        List<T> nodes = select(element, xpath, expectedNodeClass);

        if (nodes.size() < 1) {
            throw new XmlException("no match found for xpath \"" + xpath + "\"", null);
        } else if (nodes.size() > 1) {
            throw new XmlException("found " + nodes.size() + " matches for xpath \"" + xpath + "\"", null);
        }

        return nodes.get(0);
    }

    public static <T> List<T> select(Element element, String xpath, Class<T> expectedNodeClass) throws XmlException {
        try {
            XPath xp = XPathFactory.newInstance().newXPath();
            NodeList nodes = (NodeList) xp.evaluate(xpath, element, XPathConstants.NODESET);
            return toList(nodes, expectedNodeClass);
        } catch (XPathExpressionException e) {
            throw new XmlException("error in xpath \"" + xpath + "\"", e);
        }
    }

    public static <T> List<T> toList(NodeList nodes, Class<T> expectedNodeClass) {
        List<T> result = new ArrayList<T>();

        for (int i = 0; i < nodes.getLength(); i++) {
            Node node = nodes.item(i);
            result.add(expectedNodeClass.cast(node));
        }

        return result;
    }

    public static AttributeSource att(final AttributeType attType, final String value) {
        return new AttributeSource() {
            public void sendAttributes(AttributeSink sink) throws IOException {
                sink.putAttribute(attType.getName(), value);
            }
        };
    }
    
    // end of public methods

    private static DocumentBuilder newXmlParser(boolean validating, EntityResolver resolver) throws XmlException {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setValidating(validating);
            DocumentBuilder builder = factory.newDocumentBuilder();
            if (resolver != null) {
                builder.setEntityResolver(resolver);
            }
            builder.setErrorHandler(new SaxErrorHandler());
            return builder;
        } catch (ParserConfigurationException e) {
            throw new XmlException("unable to create XML parser", e);
        }
    }

    private static String makeMessageWithSnippet(SAXParseException original, String xml) {
        int wantedLineNumber = original.getLineNumber();
        if (wantedLineNumber < 1) {
            return original.getMessage();
        }

        try {
            LineNumberReader reader = makeLineNumberReader(original, xml);

            String lines = extractSnippet(reader, wantedLineNumber);

            return original.getMessage() + "\n" +
                    "error on line " + original.getLineNumber() + ":\n" +
                    lines;
        } catch (IOException e) {
            return null;
        }
    }

    private static LineNumberReader makeLineNumberReader(SAXParseException original, String xml) throws FileNotFoundException {
        if (STRING_SYSTEM_ID.equals(original.getSystemId())) {
            return new LineNumberReader(new StringReader(xml));
        } else {
            return new LineNumberReader(new FileReader(new File(original.getSystemId())));
        }
    }

    private static String extractSnippet(LineNumberReader reader, int wantedLineNumber) throws IOException {
        // skip lines above snippet
        while (reader.getLineNumber() < wantedLineNumber - 2) {
            String line = reader.readLine();
            if (line == null) return "";
        }

        StringBuilder result = new StringBuilder();
        while (reader.getLineNumber() < wantedLineNumber + 2) {
            String line = reader.readLine();
            if (line == null) break;
            new Formatter(result).format("% 4d: %s\n", reader.getLineNumber(), line);
        }

        return result.toString();
    }
    
    // inner classes

    static class SaxErrorHandler implements ErrorHandler {

        public void warning(SAXParseException exception) throws SAXException {
            throw exception;
        }

        public void error(SAXParseException exception) throws SAXException {
            throw exception;
        }

        public void fatalError(SAXParseException exception) throws SAXException {
            throw exception;
        }

    }

}

SourceForge