org.trancecode.xproc.XProcTestParser.java Source code

Java tutorial

Introduction

Here is the source code for org.trancecode.xproc.XProcTestParser.java

Source

/*
 * Copyright (C) 2010 Romain Deltour
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 */
package org.trancecode.xproc;

import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import net.sf.saxon.s9api.DocumentBuilder;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XdmNode;
import org.trancecode.logging.Logger;
import org.trancecode.xml.saxon.Saxon;
import org.trancecode.xml.saxon.SaxonAxis;
import org.trancecode.xml.saxon.SaxonPredicates;
import org.trancecode.xproc.api.PipelineException;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Parses a {http://xproc.org/ns/testsuite}test element into an
 * {@link XProcTestCase}.
 * 
 * @author Romain Deltour
 * @authro Herve Quiroz
 */
public class XProcTestParser {
    private static final Logger LOG = Logger.getLogger(XProcTestParser.class);

    private final Processor processor;
    private final Source source;
    private final String testSuite;
    private final URL url;

    private String title;
    private XdmNode description;
    private boolean ignoreWhitespace = true;
    private XdmNode pipeline;
    private final Multimap<String, List<XdmNode>> inputs = HashMultimap.create();
    private final Map<QName, String> options = Maps.newHashMap();
    private final Map<String, Map<QName, String>> parameters = Maps.newHashMap();
    private QName error;
    private final Map<String, List<XdmNode>> outputs = Maps.newHashMap();
    private XdmNode comparePipeline;

    public XProcTestParser(final Processor processor, final URL url, final String testSuite) {
        this.processor = Preconditions.checkNotNull(processor);
        this.url = url;
        this.testSuite = testSuite;
        this.source = new StreamSource(url.toString());
    }

    public void parse() {
        try {
            DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
            dfactory.setNamespaceAware(true);
            javax.xml.parsers.DocumentBuilder docBuilder;
            try {
                docBuilder = dfactory.newDocumentBuilder();
            } catch (ParserConfigurationException e) {
                throw new SaxonApiException(e);
            }
            Document doc;
            try {
                doc = docBuilder.parse(new InputSource(url.toString()));
            } catch (SAXException | IOException e) {
                throw new SaxonApiException(e);
            }
            final DocumentBuilder documentBuilder = processor.newDocumentBuilder();
            final XdmNode pipelineDocument = documentBuilder.wrap(doc);
            try {
                pipelineDocument.getUnderlyingNode().setSystemId(url.toURI().toString());
            } catch (URISyntaxException e) {
                e.printStackTrace();
            }
            final XdmNode rootNode = SaxonAxis.childElement(pipelineDocument);
            parseTest(rootNode);
        } catch (final SaxonApiException e) {
            throw new PipelineException(e);
        }
    }

    private void parseTest(final XdmNode node) {
        if (!node.getNodeName().equals(XProcTestSuiteXmlModel.ELEMENT_TEST)) {
            unsupportedElement(node);
        }
        parseTitle(SaxonAxis.childElement(node, XProcTestSuiteXmlModel.ELEMENT_TITLE));
        parseIgnoreWhitespace(node);
        parseError(node);
        parseDescription(SaxonAxis.childElements(node, XProcTestSuiteXmlModel.ELEMENT_DESCRIPTION));
        parseInputs(SaxonAxis.childElements(node, XProcTestSuiteXmlModel.ELEMENT_INPUT));
        parseOptions(SaxonAxis.childElements(node, XProcTestSuiteXmlModel.ELEMENT_OPTION));
        parseParameters(SaxonAxis.childElements(node, XProcTestSuiteXmlModel.ELEMENT_PARAMETER));
        parsePipeline(SaxonAxis.childElement(node, XProcTestSuiteXmlModel.ELEMENT_PIPELINE));
        if (error == null) {
            parseComparePipeline(SaxonAxis.childElements(node, XProcTestSuiteXmlModel.ELEMENT_COMPARE_PIPELINE));
            parseOutputs(SaxonAxis.childElements(node, XProcTestSuiteXmlModel.ELEMENT_OUTPUT));
        }

    }

    private void parseTitle(final XdmNode node) {
        title = node.getStringValue();
        LOG.trace("== Test: {} ==", title);
    }

    private void parseIgnoreWhitespace(final XdmNode node) {
        final String ignoreWhitespaceValue = node
                .getAttributeValue(XProcTestSuiteXmlModel.ATTRIBUTE_IGNORE_WHITESPACE);
        if (ignoreWhitespaceValue != null) {
            LOG.trace("Ignore whitespace: {}", ignoreWhitespaceValue);
            ignoreWhitespace = !"false".equals(ignoreWhitespaceValue);
        }
    }

    private void parseError(final XdmNode node) {
        final String errorName = node.getAttributeValue(XProcTestSuiteXmlModel.ATTRIBUTE_ERROR);
        error = (errorName != null) ? new QName(errorName, node) : null;
        if (error != null) {
            LOG.trace("Expected error: {}", error);
        }
    }

    private void parseDescription(final Iterable<XdmNode> nodes) {
        final Iterator<XdmNode> iter = nodes.iterator();
        if (iter.hasNext()) {
            description = Saxon.asDocumentNode(iter.next(), processor);
            LOG.trace("Description: parsed");
        }
        if (iter.hasNext()) {
            LOG.warn("More than one description was found");
        }
    }

    private void parseInputs(final Iterable<XdmNode> nodes) {
        for (final XdmNode node : nodes) {
            final String port = node.getAttributeValue(XProcTestSuiteXmlModel.ATTRIBUTE_PORT);
            if (port == null) {
                LOG.warn("Input with no port");
            } else {
                LOG.trace("Input for port {}", port);
                inputs.put(port, extractDocuments(node));
            }
        }
    }

    private void parseOptions(final Iterable<XdmNode> nodes) {
        for (final XdmNode node : nodes) {
            final String name = node.getAttributeValue(XProcTestSuiteXmlModel.ATTRIBUTE_NAME);
            final String value = node.getAttributeValue(XProcTestSuiteXmlModel.ATTRIBUTE_VALUE);
            if (name == null || value == null) {
                LOG.warn("Invalid option: {}={}", name, value);
            } else {
                LOG.trace("Option: {}={}", name, value);
                options.put(new QName(name, node), value);
            }
        }
    }

    private void parseParameters(final Iterable<XdmNode> nodes) {
        for (final XdmNode node : nodes) {
            final String port = node.getAttributeValue(XProcTestSuiteXmlModel.ATTRIBUTE_PORT);
            final String name = node.getAttributeValue(XProcTestSuiteXmlModel.ATTRIBUTE_NAME);
            final String value = node.getAttributeValue(XProcTestSuiteXmlModel.ATTRIBUTE_VALUE);
            if (name == null || value == null) {
                LOG.warn("Involid parameter on port '{}': {}={}", port, name, value);
            } else {
                LOG.trace("Parameter: [{}] {}={}", port, name, value);
                final String newPort = (port != null) ? port : "";
                final Map<QName, String> portParams = (parameters.containsKey(newPort)) ? parameters.get(newPort)
                        : new HashMap<>();
                portParams.put(new QName(name, node), value);
                parameters.put(newPort, portParams);
            }
        }
    }

    private void parsePipeline(final XdmNode node) {
        pipeline = extractPipeline(node);
        LOG.trace("Parsed pipeline");
    }

    private void parseComparePipeline(final Iterable<XdmNode> nodes) {
        final Iterator<XdmNode> iter = nodes.iterator();
        if (iter.hasNext()) {
            comparePipeline = extractPipeline(iter.next());
            LOG.trace("Parsed compare-pipeline");
        }
        if (iter.hasNext()) {
            LOG.warn("More than one compare-pipeline was found");
        }
    }

    private void parseOutputs(final Iterable<XdmNode> nodes) {
        for (final XdmNode node : nodes) {
            final String port = node.getAttributeValue(XProcTestSuiteXmlModel.ATTRIBUTE_PORT);
            if (port == null) {
                LOG.warn("Output with no port");
            } else {
                LOG.trace("Output for port {}", port);
                outputs.put(port, extractDocuments(node));
            }
        }
    }

    private List<XdmNode> extractDocuments(final XdmNode node) {
        final List<XdmNode> documents = Lists.newLinkedList();
        final String href = node.getAttributeValue(XProcTestSuiteXmlModel.ATTRIBUTE_HREF);
        if (href != null) {
            documents.add(loadExternalDocument(href, node));
            LOG.trace("New external document");
        } else {
            final Iterable<XdmNode> documentElements = SaxonAxis.childElements(node,
                    XProcTestSuiteXmlModel.ELEMENT_DOCUMENT);
            if (Iterables.isEmpty(documentElements)) {
                final Iterable<XdmNode> nodes = Iterables.filter(SaxonAxis.childElements(node),
                        Predicates.not(SaxonPredicates.isAttribute()));
                if (Iterables.isEmpty(nodes)) {
                    LOG.trace("New empty document");
                    documents.add(Saxon.asDocumentNode(processor, nodes));
                } else {
                    LOG.trace("New inline document");
                    final XdmNode aNode = Saxon.asDocumentNode(processor, nodes);
                    aNode.getUnderlyingNode().setSystemId(node.getBaseURI().toASCIIString());
                    documents.add(aNode);
                }
            } else {
                for (final XdmNode documentNode : documentElements) {
                    final String documentHref = documentNode
                            .getAttributeValue(XProcTestSuiteXmlModel.ATTRIBUTE_HREF);
                    if (documentHref != null) {
                        LOG.trace("New external document: {}", documentHref);
                        documents.add(loadExternalDocument(documentHref, documentNode));
                    } else {
                        LOG.trace("New wrapped document");
                        final Iterable<XdmNode> nodesWithoutAttributes = Iterables.filter(
                                SaxonAxis.childNodes(documentNode), Predicates.not(SaxonPredicates.isAttribute()));
                        documents.add(Saxon.asDocumentNode(processor, nodesWithoutAttributes));
                    }
                }
            }
        }
        return documents;
    }

    private XdmNode extractPipeline(final XdmNode node) {
        final String href = node.getAttributeValue(XProcTestSuiteXmlModel.ATTRIBUTE_HREF);
        if (href != null) {
            LOG.trace("New external document");
            return loadExternalDocument(href, node);
        } else {
            return Saxon.asDocumentNode(SaxonAxis.childElement(node), processor);
        }
    }

    private XdmNode loadExternalDocument(final String href, final XdmNode node) {
        final DocumentBuilder builder = processor.newDocumentBuilder();
        final URI uri = node.getBaseURI().resolve(href);
        final SAXSource source = new SAXSource(new InputSource(uri.toString()));
        try {
            final XdmNode aNode = builder.build(source);
            aNode.getUnderlyingNode().setSystemId(uri.toASCIIString());
            return aNode;
        } catch (final SaxonApiException e) {
            throw new IllegalStateException("Couldn't load document at " + uri);
        }
    }

    public XProcTestCase getTest() {
        return new XProcTestCase(url, testSuite, title, description, ignoreWhitespace, pipeline, inputs, options,
                parameters, error, outputs, comparePipeline);
    }

    private void unsupportedElement(final XdmNode node) {
        throw new IllegalStateException("Unsupported element " + node.getNodeName().toString());
    }
}