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

org.n52.svalbard.encode.stream.xml.AbstractElementXmlStreamWriter Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
/*
 * Copyright 2016-2019 52°North Initiative for Geospatial Open Source
 * Software GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.n52.svalbard.encode.stream.xml;

import static java.util.stream.Collectors.joining;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.net.URI;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;

import org.n52.iceland.util.XmlFactories;
import org.n52.shetland.ogc.gml.time.TimeInstant;
import org.n52.shetland.ogc.gml.time.TimePosition;
import org.n52.shetland.util.DateTimeHelper;
import org.n52.shetland.w3c.SchemaLocation;
import org.n52.shetland.w3c.W3CConstants;
import org.n52.shetland.w3c.xlink.Actuate;
import org.n52.shetland.w3c.xlink.Link;
import org.n52.shetland.w3c.xlink.Show;
import org.n52.svalbard.encode.exception.EncodingException;
import org.n52.svalbard.stream.XLinkConstants;

import com.google.common.base.Strings;
import com.google.common.escape.Escaper;
import com.google.common.io.BaseEncoding;
import com.google.common.io.ByteStreams;
import com.google.common.xml.XmlEscapers;

public abstract class AbstractElementXmlStreamWriter extends XmlFactories implements ElementXmlStreamWriter {

    private static final Escaper ESCAPER = XmlEscapers.xmlContentEscaper();

    private XmlStreamWritingContext context;

    @Override
    public void setContext(XmlStreamWritingContext context) {
        this.context = Objects.requireNonNull(context);
    }

    protected XmlStreamWritingContext context() {
        return this.context;
    }

    protected void attr(QName name,
            String value) throws XMLStreamException {
        context().dispatch(eventFactory().createAttribute(name, value));
    }

    protected void attr(QName name,
            Optional value) throws XMLStreamException {
        if (value.isPresent()) {
            attr(name, value.get());
        }
    }

    protected void attr(String name,
            Optional value) throws XMLStreamException {
        attr(new QName(name), value);
    }

    protected void attr(String name,
            String value) throws XMLStreamException {
        attr(new QName(name), value);
    }

    protected  void attr(QName name,
            Collection coll,
            Function mapper) throws XMLStreamException {
        if (coll != null && !coll.isEmpty()) {
            attr(name, coll.stream().map(mapper).collect(joining(" ")));
        }
    }

    protected void attr(String namespace,
            String localName,
            String value) throws XMLStreamException {
        attr(new QName(namespace, localName), value);
    }

    protected  void attr(String name,
            Collection coll,
            Function mapper) throws XMLStreamException {
        attr(new QName(name), coll, mapper);
    }

    protected void namespace(String prefix,
            String namespace) throws XMLStreamException {
        if (context().declareNamespace(prefix, namespace)) {
            context().dispatch(eventFactory().createNamespace(prefix, namespace));
        }
    }

    protected void start(String namespace,
            String localName) throws XMLStreamException {
        start(new QName(namespace, localName));
    }

    protected void start(String namespace,
            String localName,
            String prefix) throws XMLStreamException {
        start(new QName(namespace, localName, prefix));
    }

    protected void start(QName name) throws XMLStreamException {
        context().dispatch(eventFactory().createStartElement(name.getPrefix(), name.getNamespaceURI(), name
                .getLocalPart()));
    }

    protected void empty(QName name) throws XMLStreamException {
        start(name);
        end(name);
    }

    protected void chars(String chars) throws XMLStreamException {
        context().dispatch(eventFactory().createCharacters(chars));
    }

    protected void chars(String chars,
            boolean escape) throws XMLStreamException {
        // TODO escape by default
        context().dispatch(eventFactory().createCharacters(escape ? escaper().escape(chars) : chars));
    }

    protected void end(QName name) throws XMLStreamException {
        context().dispatch(eventFactory().createEndElement(name.getPrefix(), name.getNamespaceURI(), name
                .getLocalPart()));
    }

    protected void write(Reader in) throws XMLStreamException {
        context().write(in);
    }

    protected void write(XMLEventReader reader) throws XMLStreamException {
        context().write(reader);
    }

    protected void flush() throws XMLStreamException {
        context().flush();
    }

    protected void cdata(String value) throws XMLStreamException {
        context().dispatch(eventFactory().createCData(value));
    }

    protected void writeBase64(InputStream data) throws IOException {
        try (CharacterEmittingWriter writer = new CharacterEmittingWriter();
                OutputStream encodingStream = base64().encodingStream(writer)) {
            ByteStreams.copy(data, encodingStream);
        }
    }

    /**
     * Write {@link TimeInstant} to stream
     *
     * @param time
     *            {@link TimeInstant} to write to stream
     *
     * @throws XMLStreamException
     *             If an error occurs when writing to {@link OutputStream}
     */
    protected void time(TimeInstant time) throws XMLStreamException {
        time(time.getTimePosition());
    }

    /**
     * Write {@link TimePosition} as ISO 8601 to stream
     *
     * @param time
     *            {@link TimePosition} to write as ISO 8601 to stream
     *
     * @throws XMLStreamException
     *             If an error occurs when writing to {@link OutputStream}
     */
    protected void time(TimePosition time) throws XMLStreamException {
        chars(DateTimeHelper.formatDateTime2IsoString(time.getTime()));
    }

    /**
     * Write {@link SchemaLocation}s as xsi:schemaLocations attribute to stream
     *
     * @param schemaLocations
     *            {@link SchemaLocation}s to write
     *
     * @throws XMLStreamException
     *             If an error occurs when writing to {@link OutputStream}
     */
    protected void schemaLocation(Set schemaLocations) throws XMLStreamException {
        String merged = mergeSchemaLocationsToString(schemaLocations);
        if (!Strings.isNullOrEmpty(merged)) {
            namespace(W3CConstants.NS_XSI_PREFIX, W3CConstants.NS_XSI);
            attr(W3CConstants.QN_SCHEMA_LOCATION_PREFIXED, merged);
        }
    }

    protected void writeXLinkAttrs(Link link) throws XMLStreamException {
        namespace(XLinkConstants.NS_XLINK_PREFIX, XLinkConstants.NS_XLINK);
        attr(XLinkConstants.Attr.QN_HREF, link.getHref().map(URI::toString));
        attr(XLinkConstants.Attr.QN_ROLE, link.getRole().map(URI::toString));
        attr(XLinkConstants.Attr.QN_SHOW, link.getShow().map(Show::toString));
        attr(XLinkConstants.Attr.QN_TITLE, link.getTitle());
        attr(XLinkConstants.Attr.QN_ARCROLE, link.getArcrole().map(URI::toString));
        attr(XLinkConstants.Attr.QN_ACTUATE, link.getActuate().map(Actuate::toString));
    }

    protected void writeLink(QName name,
            Link link) throws XMLStreamException {
        element(name, link, this::writeXLinkAttrs);
    }

    protected void element(QName name,
            String value) throws XMLStreamException {
        start(name);
        chars(value);
        end(name);
    }

    protected void element(QName name,
            Optional value) throws XMLStreamException {
        if (value.isPresent()) {
            element(name, value.get());
        }
    }

    protected void element(QName name,
            OffsetDateTime time) throws XMLStreamException {
        element(name, format(time));
    }

    protected  void element(QName name,
            Optional elem,
            ElementWriter writer) throws XMLStreamException {
        if (elem.isPresent()) {
            start(name);
            writer.write(elem.get());
            end(name);
        }
    }

    protected  void element(QName name,
            T elem,
            ElementWriter writer) throws XMLStreamException {
        if (elem != null) {
            start(name);
            writer.write(elem);
            end(name);
        }
    }

    protected  void element(QName name,
            ContentWriter writer) throws XMLStreamException {
        start(name);
        writer.write();
        end(name);
    }

    protected  void delegate(T object) throws XMLStreamException, EncodingException {
        context().write(object);
    }

    protected String format(OffsetDateTime time) {
        return DateTimeFormatter.ISO_DATE_TIME.format(time);
    }

    protected  void forEach(QName name,
            Iterable elements,
            ElementWriter writer) throws XMLStreamException {
        if (elements != null) {
            for (T elem : elements) {
                start(name);
                writer.write(elem);
                end(name);
            }
        }
    }

    protected void writeXML(String xml) throws XMLStreamException {
        try (StringReader reader = new StringReader(xml)) {
            write(reader);
        }
    }

    protected static BaseEncoding base64() {
        return BaseEncoding.base64().withSeparator("\n", 80);
    }

    private static String mergeSchemaLocationsToString(Iterable schemaLocations) {
        if (schemaLocations == null) {
            return "";
        }
        StringBuilder builder = new StringBuilder();
        Iterator it = schemaLocations.iterator();
        if (it.hasNext()) {
            builder.append(it.next().getSchemaLocationString());
            while (it.hasNext()) {
                builder.append(" ").append(it.next().getSchemaLocationString());
            }
        }
        return builder.toString();
    }

    /**
     * @return the xmlContentEscaper
     */
    protected static Escaper escaper() {
        return ESCAPER;
    }

    @FunctionalInterface
    protected interface ElementWriter {
        void write(T elem) throws XMLStreamException;
    }

    @FunctionalInterface
    protected interface ContentWriter {
        void write() throws XMLStreamException;
    }

    private class CharacterEmittingWriter extends Writer {
        private boolean closed;

        @Override
        public void write(char[] cbuf,
                int off,
                int len) throws IOException {
            checkNotClosed();
            write(new String(cbuf, off, len));
        }

        @Override
        public void write(String str) throws IOException {
            checkNotClosed();
            try {
                AbstractElementXmlStreamWriter.this.chars(str);
            } catch (XMLStreamException ex) {
                throw new IOException(ex);
            }
        }

        @Override
        public void write(String str,
                int off,
                int len) throws IOException {
            checkNotClosed();
            write(str.substring(off, len));
        }

        @Override
        public void flush() throws IOException {
            checkNotClosed();
            try {
                AbstractElementXmlStreamWriter.this.flush();
            } catch (XMLStreamException ex) {
                throw new IOException(ex);
            }
        }

        @Override
        public void close() {
            this.closed = true;
        }

        private void checkNotClosed() throws IOException {
            if (closed) {
                throw new IOException("already closed");
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy