# 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;
        }
    }