org.trancecode.xml.saxon.SaxonBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.trancecode.xml.saxon.SaxonBuilder.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.xml.saxon;

import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import net.sf.saxon.Configuration;
import net.sf.saxon.event.NamespaceReducer;
import net.sf.saxon.event.Receiver;
import net.sf.saxon.event.TreeReceiver;
import net.sf.saxon.om.FingerprintedQName;
import net.sf.saxon.om.NamePool;
import net.sf.saxon.om.NamespaceBinding;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.om.StandardNames;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XdmDestination;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.linked.AttributeImpl;
import net.sf.saxon.tree.linked.CommentImpl;
import net.sf.saxon.tree.linked.ElementImpl;
import net.sf.saxon.tree.linked.ProcInstImpl;
import net.sf.saxon.tree.linked.TextImpl;
import net.sf.saxon.tree.util.NamespaceIterator;
import net.sf.saxon.type.BuiltInAtomicType;
import net.sf.saxon.type.BuiltInType;

import java.util.Iterator;

//import net.sf.saxon.tree.iter.NamespaceIterator;

/**
 * A builder to create new XdmNode documents using a push API. It provides a
 * facade to the lower-level Saxon Receiver API.
 * 
 * @see Receiver
 * @author Romain Deltour
 */
public class SaxonBuilder {
    private final XdmDestination destination = new XdmDestination();
    private final NamespaceReducer receiver;
    private final NamePool namePool;

    /**
     * Creates a new builder based on the given Saxon configuration.
     * 
     * @param configuration
     *            The Saxon configuration. In particular, the name pool of the
     *            configuration will be used to construct the new item codes.
     */
    public SaxonBuilder(final Configuration configuration) {
        try {
            receiver = new NamespaceReducer(new TreeReceiver(destination.getReceiver(configuration)));
            receiver.setPipelineConfiguration(configuration.makePipelineConfiguration());
            namePool = configuration.getNamePool();
            receiver.open();
        } catch (final SaxonApiException | XPathException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Starts a document node.
     */
    public void startDocument() {
        try {
            receiver.startDocument(0);
        } catch (final XPathException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Ends the document node.
     */
    public void endDocument() {
        try {
            receiver.endDocument();
        } catch (final XPathException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Starts a new element with the given QName.
     * 
     * @param qname
     *            The QName of the new element.
     */
    public void startElement(final QName qname) {
        try {
            ElementImpl element = new ElementImpl();
            element.setNodeName(new FingerprintedQName(qname.getStructuredQName(), namePool));
            receiver.startElement(element.getNodeName(), BuiltInType.getSchemaType(StandardNames.XS_UNTYPED),
                    element, 0);
        } catch (final XPathException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Start a new element with the given QName, and adds the in-scope
     * namespaces of the given node to the new element.
     * 
     * @param qname
     *            The QName of the new element.
     * @param nsContext
     *            A node whose in-scope namespaces are copied to the new
     *            element.
     */
    public void startElement(final QName qname, final XdmNode nsContext) {
        try {
            startElement(qname);
            final Iterator<NamespaceBinding> inscopeNsCodes = NamespaceIterator
                    .iterateNamespaces(nsContext.getUnderlyingNode());
            while (inscopeNsCodes.hasNext()) {
                NamespaceBinding namespaceBinding = inscopeNsCodes.next();
                receiver.namespace(namespaceBinding, 0);
            }
        } catch (final XPathException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Ends the current element node.
     */
    public void endElement() {
        try {
            receiver.endElement();
        } catch (final XPathException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Starts the content of the current element. Must be called after the
     * declaration of namespaces and attributes, before adding text or node
     * children to the element.
     */
    public void startContent() {
        try {
            receiver.startContent();
        } catch (final XPathException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Adds an attribute to the current element.
     * 
     * @param qname
     *            The QName of the attribute
     * @param value
     *            The value of the attribute
     */
    public void attribute(final QName qname, final String value) {
        try {
            AttributeImpl attributeNode = new AttributeImpl(null, 0);
            receiver.attribute(
                    new FingerprintedQName(qname.getPrefix(), qname.getNamespaceURI(), qname.getLocalName()),
                    BuiltInAtomicType.UNTYPED_ATOMIC, value, attributeNode, 0);

        } catch (final XPathException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Adds a new comment node.
     * 
     * @param comment
     *            The comment text
     */
    public void comment(final String comment) {
        try {
            CommentImpl commentNode = new CommentImpl(comment);
            receiver.comment(comment, commentNode, 0);
        } catch (final XPathException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Declares a new namespace in the current element
     * 
     * @param prefix
     *            The namespace prefix
     * @param uri
     *            The namespace URI
     */
    public void namespace(final String prefix, final String uri) {
        try {
            receiver.namespace(NamespaceBinding.makeNamespaceBinding((prefix != null) ? prefix : "", uri), 0);
        } catch (final XPathException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Appends the given nodes to the document being built.
     */
    public void nodes(final XdmNode... nodes) {
        nodes(ImmutableList.copyOf(nodes));
    }

    /**
     * Appends the given nodes to the document being built.
     */
    public void nodes(final Iterable<XdmNode> nodes) {
        try {
            for (final XdmNode node : nodes) {
                receiver.append(node.getUnderlyingNode(), node.getUnderlyingNode().saveLocation(),
                        NodeInfo.LOCAL_NAMESPACES);
            }
        } catch (final XPathException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Adds a processing instruction.
     * 
     * @param name
     *            The processing instruction name
     * @param data
     *            The processing instruction data
     */
    public void processingInstruction(final String name, final String data) {
        try {
            ProcInstImpl procInst = new ProcInstImpl(name, data);
            receiver.processingInstruction(name, data, procInst, 0);
        } catch (final XPathException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Adds a text node
     * 
     * @param text
     *            The text content
     */
    public void text(final String text) {
        try {
            TextImpl textNode = new TextImpl(text);
            receiver.characters(text, textNode, 0);
        } catch (final XPathException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Adds a raw XML fragment
     * 
     * @param raw
     *            The XML fragment
     */
    public void raw(final String raw, final Processor processor) {
        Preconditions.checkNotNull(processor);
        Preconditions.checkNotNull(raw);

        final String toBeParsed = "<wrapperElement>" + raw + "</wrapperElement>";
        final XdmNode parsedNode = Saxon.parse(toBeParsed, processor);
        final XdmNode rootNode = Iterables.getOnlyElement(SaxonAxis.childNodes(parsedNode));

        // Re-ask for subnodes as the first childNodes() call will give the
        // wrapperElement element
        final Iterable<XdmNode> subNodesList = SaxonAxis.childNodes(rootNode);
        final Iterable<XdmNode> filteredSubNodesList = Iterables.filter(subNodesList,
                Predicates.not(SaxonPredicates.isIgnorableWhitespace()));

        nodes(filteredSubNodesList);
    }

    /**
     * Returns the result node built by this builder.
     */
    public XdmNode getNode() {
        return destination.getXdmNode();
    }
}