CustomLSInput.java

package org.thewonderlemming.c4plantuml.graphml.validation;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.thewonderlemming.c4plantuml.graphml.Build;
import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSResourceResolver;

/**
 * A basic implementation of {@link LSInput}, in order to implement our custom {@link LSResourceResolver}.
 *
 * @author thewonderlemming
 *
 */
public class CustomLSInput implements LSInput {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomLSInput.class);

    private static final int ONE_KBYTE = 1024;

    private String baseURI;

    private String publicId;

    private String resourceDataAsString;

    private String systemId;


    /**
     * A fluent builder to ease the construction of {@link CustomLSInput}, since its constructor contains several
     * arguments which are all strings.
     *
     * @return a new instance of {@link CustomLSInput}.
     */
    public static WithBaseURI<WithPublicId<WithResourceFilename<WithSystemId<Build<CustomLSInput>>>>> newBuilder() {

        return baseURI -> publicId -> resourceFilename -> systemId -> () -> new CustomLSInput(
            baseURI,
                publicId,
                resourceFilename,
                systemId);
    }

    private static final void printUnsupportedOperation(final String methodName) {
        LOGGER.warn("Unsupported operation {}. The call will be ignored.", methodName);
    }

    private static String readResourceContentAsString(final InputStream resourceStream) {

        try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {

            final byte[] buffer = new byte[ONE_KBYTE];
            int length;

            while ((length = resourceStream.read(buffer)) != -1) {
                baos.write(buffer, 0, length);
            }

            return baos.toString(StandardCharsets.UTF_8.name());

        } catch (final UnsupportedEncodingException e) {
            LOGGER.error("Unsupported encoding exception should not occur here with encoding 'UTF_8'!");

        } catch (final IOException e) {
            LOGGER.error("Could not retrieve the LSInput because of the following: {}", e.getMessage(), e);
        }

        return "";
    }

    private static String readResourceContentAsString(final String resourceFilename) {

        try (final InputStream is = CustomLSInput.class.getResourceAsStream(resourceFilename)) {
            return readResourceContentAsString(is);

        } catch (final IOException e) {
            LOGGER.error("Could not retrieve the LSInput because of the following: {}", e.getMessage(), e);
        }

        return "";
    }

    private CustomLSInput(final String baseURI, final String publicId, final String resourceFilename,
        final String systemId) {

        this.baseURI = baseURI;
        this.publicId = publicId;
        this.systemId = systemId;
        this.resourceDataAsString = readResourceContentAsString(resourceFilename);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getBaseURI() {
        return this.baseURI;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public InputStream getByteStream() {
        return new ByteArrayInputStream(this.resourceDataAsString.getBytes());
    }

    /**
     * {@inheritDoc}
     * <p>
     * Unsupported operation.
     */
    @Override
    public boolean getCertifiedText() {
        printUnsupportedOperation("getCertifiedText");
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Reader getCharacterStream() {


        try (final InputStream bais = new ByteArrayInputStream(this.resourceDataAsString.getBytes())) {

            return new InputStreamReader(bais, StandardCharsets.UTF_8);

        } catch (final IOException e) {
            LOGGER
                .error("Could not return character stream for {} because of the following: {}",
                    this.publicId,
                    e.getMessage(),
                    e);
        }

        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getEncoding() {
        return StandardCharsets.UTF_8.name();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getPublicId() {
        return this.publicId;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getStringData() {
        return this.resourceDataAsString;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getSystemId() {
        return this.systemId;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setBaseURI(final String baseURI) {
        this.baseURI = baseURI;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setByteStream(final InputStream byteStream) {

        try (final BufferedInputStream bis = new BufferedInputStream(byteStream)) {
            this.resourceDataAsString = bis.toString();

        } catch (final IOException e) {
            LOGGER.error("Could not set byte stream because of the following: {}", e.getMessage(), e);
        }
    }

    /**
     * {@inheritDoc}
     * <p>
     * Unsupported operation.
     */
    @Override
    public void setCertifiedText(final boolean certifiedText) {
        printUnsupportedOperation("setCertifiedText");
    }

    /**
     * {@inheritDoc}
     * <p>
     * Unsupported operation.
     */
    @Override
    public void setCharacterStream(final Reader characterStream) {
        printUnsupportedOperation("setCharacterStream");
    }

    /**
     * {@inheritDoc}
     * <p>
     * Unsupported operation.
     */
    @Override
    public void setEncoding(final String encoding) {
        printUnsupportedOperation("setEncoding");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setPublicId(final String publicId) {
        this.publicId = publicId;
    }

    /**
     * {@inheritDoc}
     * <p>
     * Unsupported operation.
     */
    @Override
    public void setStringData(final String stringData) {
        printUnsupportedOperation("setStringData");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setSystemId(final String systemId) {
        this.systemId = systemId;
    }
}