All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xs4j.xmlspitter.XMLSpitterImpl Maven / Gradle / Ivy

Go to download

An attempt to port parsing capabilities offered by Groovy XMLSlurper into the Java world. The following is not planned to be accurate projection, instead the most useful functions will be implemented.

The newest version!
package org.xs4j.xmlspitter;

import org.xs4j.XMLNode;
import org.xs4j.xmlslurper.NodeListener;
import org.xs4j.xmlslurper.Slurp;
import org.xs4j.util.NotNull;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.OutputStream;
import java.io.Writer;
import java.util.ArrayDeque;
import java.util.Deque;

import static org.xs4j.xmlspitter.OutputSupplierFactory.GenericOutputSupplier.ILLEGAL_SUPPLIER_ARGUMENT;
import static org.xs4j.xmlspitter.XMLSpitterFactory.DEFAULT_XML_DOCUMENT_ENCODING;
import static org.xs4j.xmlspitter.XMLSpitterFactory.DEFAULT_XML_DOCUMENT_VERSION;
import static org.xs4j.util.NonNullValidator.requireNonNull;

/**
 * Created by mturski on 12/8/2016.
 */
public class XMLSpitterImpl implements XMLSpitter {
    private static final String NEWLINE = "\n";
    private static final String INDENT = "    ";

    private static long idFeed;

    private final StreamProvider streamProvider;


    XMLSpitterImpl(StreamProvider streamProvider) {
        idFeed = 0L;

        this.streamProvider = streamProvider;
    }

    @Override
    public void write(@NotNull Slurp documentNode, @NotNull Slurp contentNodes, @NotNull final OutputSupplier outputSupplier) {
        startWriteOne(documentNode, contentNodes, outputSupplier, DEFAULT_XML_DOCUMENT_ENCODING, DEFAULT_XML_DOCUMENT_VERSION);
    }

    @Override
    public void write(@NotNull Slurp documentNode, @NotNull Slurp contentNodes, @NotNull final OutputSupplier outputSupplier, @NotNull final String version) {
        startWriteOne(documentNode, contentNodes, outputSupplier, DEFAULT_XML_DOCUMENT_ENCODING, version);
    }

    @Override
    public void write(@NotNull Slurp documentNode, @NotNull Slurp contentNodes, @NotNull final OutputSupplier outputSupplier, @NotNull final String version, @NotNull final String encoding) {
        startWriteOne(documentNode, contentNodes, outputSupplier, encoding, version);
    }

    @Override
    public void writeAll(@NotNull Slurp documentNode, @NotNull Slurp contentNodes, @NotNull final OutputSupplier outputSupplier) {
        startWriteAll(documentNode, contentNodes, outputSupplier, DEFAULT_XML_DOCUMENT_ENCODING, DEFAULT_XML_DOCUMENT_VERSION);
    }

    @Override
    public void writeAll(@NotNull Slurp documentNode, @NotNull Slurp contentNodes, @NotNull final OutputSupplier outputSupplier, final String version) {
        startWriteAll(documentNode, contentNodes, outputSupplier, DEFAULT_XML_DOCUMENT_ENCODING, version);
    }

    @Override
    public void writeAll(@NotNull Slurp documentNode, @NotNull Slurp contentNodes, @NotNull final OutputSupplier outputSupplier, @NotNull final String version, @NotNull final String encoding) {
        startWriteAll(documentNode, contentNodes, outputSupplier, encoding, version);
    }

    @Override
    public XMLStream createStream(@NotNull OutputStream outputStream) {
        try {
            return streamProvider.getStream(outputStream);
        } catch (XMLStreamException e) {
            throw new XMLStreamRuntimeException(e);
        }
    }

    @Override
    public XMLStream createStream(@NotNull Writer writer) {
        try {
            return streamProvider.getStream(writer);
        } catch (XMLStreamException e) {
            throw new XMLStreamRuntimeException(e);
        }
    }

    private void startWriteOne(Slurp documentNode, Slurp contentNodes, final OutputSupplier osSupplier, final String encoding, final String version) {
        requireNonNull(documentNode);
        requireNonNull(contentNodes);
        requireNonNull(osSupplier);
        requireNonNull(encoding);
        requireNonNull(version);

        final XMLStream[] streams = new XMLStream[1];
        final Deque descendants = new ArrayDeque();

        documentNode.find(
                new StartDocumentHandler(streams, descendants, osSupplier, encoding, version),
                new EndDocumentHandler(streams, descendants));
        contentNodes.findAll(
                new StartContentHandler(streams, descendants),
                new EndContentHandler(streams, descendants));
    }

    private void startWriteAll(Slurp documentNode, Slurp contentNodes, final OutputSupplier osSupplier, final String encoding, final String version) {
        requireNonNull(documentNode);
        requireNonNull(contentNodes);
        requireNonNull(osSupplier);
        requireNonNull(encoding);
        requireNonNull(version);

        final XMLStream[] streams = new XMLStream[1];
        final Deque descendants = new ArrayDeque();

        documentNode.findAll(
                new StartDocumentHandler(streams, descendants, osSupplier, encoding, version),
                new EndDocumentHandler(streams, descendants));
        contentNodes.findAll(
                new StartContentHandler(streams, descendants),
                new EndContentHandler(streams, descendants));
    }

    private class StartDocumentHandler implements NodeListener {
        private final XMLStream[] streams;
        private final Deque descendants;
        private final OutputSupplier osSupplier;
        private final String encoding;
        private final String version;

        private StartDocumentHandler(XMLStream[] streams, Deque descendants, OutputSupplier osSupplier, String encoding, String version) {
            this.streams = streams;
            this.descendants = descendants;
            this.osSupplier = osSupplier;
            this.encoding = encoding;
            this.version = version;
        }

        @Override
        public void onNode(@NotNull XMLNode node) {
            try {
                Object output = osSupplier.supply();
                if (output instanceof Writer)
                    streams[0] = streamProvider.getStream((Writer)output);
                else if (output instanceof OutputStream)
                    streams[0] = streamProvider.getStream((OutputStream)output);
                else
                    throw new IllegalArgumentException(String.format(ILLEGAL_SUPPLIER_ARGUMENT, OutputSupplier.class.getName(), Writer.class.getName(), OutputStream.class.getName()));

                streams[0].writeStartDocument(encoding, version);
                streams[0].writeCharacters(NEWLINE);
                streams[0].writeStartElement(node);

                descendants.addLast(node);
            } catch (XMLStreamException e) {
                throw new XMLStreamRuntimeException(e);
            }
        }
    }

    private class EndDocumentHandler implements NodeListener {
        private final XMLStream[] streams;
        private final Deque descendants;

        private EndDocumentHandler(XMLStream[] streams, Deque descendants) {
            this.streams = streams;
            this.descendants = descendants;
        }

        @Override
        public void onNode(@NotNull XMLNode node) {
            descendants.removeLast();

            String characters = node.getText();
            if (characters != null && !characters.isEmpty()) {
                streams[0].writeCharacters(characters);
            } else {
                streams[0].writeCharacters(NEWLINE);
            }
            streams[0].writeEndElement();
            streams[0].close();
            streams[0] = null;
        }
    }

    private class StartContentHandler implements NodeListener {
        private final XMLStream[] streams;
        private final Deque descendants;

        private StartContentHandler(XMLStream[] streams, Deque descendants) {
            this.streams = streams;
            this.descendants = descendants;
        }

        @Override
        public void onNode(@NotNull XMLNode node) {
            streams[0].writeCharacters(NEWLINE);
            formatIndent(streams[0], descendants);
            streams[0].writeStartElement(node);

            descendants.addLast(node);
        }
    }

    private class EndContentHandler implements NodeListener {
        private final XMLStream[] streams;
        private final Deque descendants;

        private EndContentHandler(XMLStream[] streams, Deque descendants) {
            this.streams = streams;
            this.descendants = descendants;
        }

        @Override
        public void onNode(@NotNull XMLNode node) {
            descendants.removeLast();

            String characters = node.getText();
            if (characters != null && !characters.isEmpty()) {
                streams[0].writeCharacters(characters);
            } else {
                streams[0].writeCharacters(NEWLINE);
                formatIndent(streams[0], descendants);
            }
            streams[0].writeEndElement();
            streams[0].flush();
        }
    }

    interface StreamProvider {
        XMLStream getStream(OutputStream outputStream) throws XMLStreamException;

        XMLStream getStream(Writer writer) throws XMLStreamException;
    }

    static class InternalStreamProvider implements StreamProvider {
        @Override
        public XMLStream getStream(OutputStream outputStream) throws XMLStreamException {
            return new InternalStream(idFeed++, outputStream);
        }

        @Override
        public XMLStream getStream(Writer writer) throws XMLStreamException {
            return new InternalStream(idFeed++, writer);
        }
    }

    static class StAXStreamProvider implements StreamProvider {
        private static final String DEFAULT_ENCODING = "UTF-8";

        private final XMLOutputFactory xmlOutputFactory;

        StAXStreamProvider(XMLOutputFactory xmlOutputFactory) {
            this.xmlOutputFactory = xmlOutputFactory;
        }

        @Override
        public XMLStream getStream(OutputStream outputStream) throws XMLStreamException {
            XMLStreamWriter stream = xmlOutputFactory.createXMLStreamWriter(outputStream, DEFAULT_ENCODING);

            return new StAXStream(idFeed++, stream);
        }

        @Override
        public XMLStream getStream(Writer writer) throws XMLStreamException {
            XMLStreamWriter stream = xmlOutputFactory.createXMLStreamWriter(writer);

            return new StAXStream(idFeed++, stream);
        }
    }

    @SuppressWarnings("unused")
    private static void formatIndent(XMLStream stream, Deque descendants) {
        for(XMLNode descendant : descendants)
            stream.writeCharacters(INDENT);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy