/* UtilLib - Copyright (c) 2009-2023 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.util.xml;

import java.io.IOException;


/**
 *
 * @author Vincent Calame
 */
public class DefaultXMLWriter implements AppendableXMLWriter {

    private Appendable appendable;
    private int indentLength = 0;
    private boolean aposEscaped = true;

    public DefaultXMLWriter() {
    }

    public void setAppendable(Appendable appendable) {
        this.appendable = appendable;
    }

    public Appendable getAppendable() {
        return appendable;
    }

    @Override
    public int getCurrentIndentValue() {
        return indentLength;
    }

    public void setIndentLength(int indentLength) {
        this.indentLength = indentLength;
    }

    public void increaseIndentValue() {
        indentLength++;
    }

    public void decreaseIndentValue() {
        indentLength--;
    }

    public boolean isAposEscaped() {
        return aposEscaped;
    }

    public void setAposEscaped(boolean aposEscaped) {
        this.aposEscaped = aposEscaped;
    }

    @Override
    public DefaultXMLWriter appendXMLDeclaration() throws IOException {
        XMLUtils.appendXmlDeclaration(this);
        return this;
    }

    @Override
    public DefaultXMLWriter addEmptyElement(String tagname) throws IOException {
        return (DefaultXMLWriter) AppendableXMLWriter.super.addEmptyElement(tagname);
    }

    @Override
    public DefaultXMLWriter addSimpleElement(String tagname, String value) throws IOException {
        return (DefaultXMLWriter) AppendableXMLWriter.super.addSimpleElement(tagname, value);
    }

    @Override
    public DefaultXMLWriter addSimpleElement(String tagname, String value, String lang) throws IOException {
        return (DefaultXMLWriter) AppendableXMLWriter.super.addSimpleElement(tagname, value, lang);
    }

    @Override
    public DefaultXMLWriter startOpenTag(String name) throws IOException {
        startOpenTag(name, true);
        return this;
    }

    @Override
    public DefaultXMLWriter startOpenTag(String name, boolean indentBefore) throws IOException {
        if (indentBefore) {
            appendIndent();
        }
        append('<');
        append(name);
        return this;
    }

    @Override
    public DefaultXMLWriter endOpenTag() throws IOException {
        append('>');
        increaseIndentValue();
        return this;
    }

    @Override
    public DefaultXMLWriter closeEmptyTag() throws IOException {
        append('/');
        append('>');
        return this;
    }

    @Override
    public DefaultXMLWriter openTag(String name) throws IOException {
        openTag(name, true);
        return this;
    }

    @Override
    public DefaultXMLWriter openTag(String name, boolean indentBefore) throws IOException {
        if (indentBefore) {
            appendIndent();
        }
        append('<');
        append(name);
        append('>');
        increaseIndentValue();
        return this;
    }

    @Override
    public DefaultXMLWriter closeTag(String name) throws IOException {
        closeTag(name, true);
        return this;
    }

    @Override
    public DefaultXMLWriter closeTag(String name, boolean indentBefore) throws IOException {
        decreaseIndentValue();
        if (indentBefore) {
            appendIndent();
        }
        append('<');
        append('/');
        append(name);
        append('>');
        return this;
    }

    @Override
    public DefaultXMLWriter addText(CharSequence charSequence) throws IOException {
        if (charSequence == null) {
            return this;
        }
        int length = charSequence.length();
        for (int i = 0; i < length; i++) {
            char carac = charSequence.charAt(i);
            escape(carac);
        }
        return this;
    }

    private void escape(char carac) throws IOException {
        if (carac < 32) {
            append(' ');
        } else {
            switch (carac) {
                case '&':
                    append("&amp;");
                    break;
                case '"':
                    append("&quot;");
                    break;
                case '\'':
                    if (aposEscaped) {
                        append("&apos;");
                    } else {
                        append(carac);
                    }
                    break;
                case '<':
                    append("&lt;");
                    break;
                case '>':
                    append("&gt;");
                    break;
                case '\u00A0':
                    append("&#x00A0;");
                    break;
                case '\u202F':
                    append("&#x202F;");
                    break;
                default:
                    append(carac);
            }
        }
    }

    @Override
    public DefaultXMLWriter append(char c) throws IOException {
        appendable.append(c);
        return this;
    }

    @Override
    public DefaultXMLWriter append(CharSequence charSequence) throws IOException {
        appendable.append(charSequence);
        return this;
    }

    @Override
    public DefaultXMLWriter append(CharSequence charSequence, int start, int end) throws IOException {
        appendable.append(charSequence, start, end);
        return this;
    }

    @Override
    public DefaultXMLWriter addCData(CharSequence charSequence) throws IOException {
        appendable.append("<![CDATA[");
        int length = charSequence.length();
        for (int i = 0; i < length; i++) {
            char carac = charSequence.charAt(i);
            if (carac == ']') {
                if ((i < (length - 1)) && (charSequence.charAt(i + 1) == ']')) {
                    appendable.append("]]>");
                    appendable.append("]]");
                    appendable.append("<![CDATA[");
                    i = i + 1;
                } else {
                    appendable.append(']');
                }
            } else {
                appendable.append(carac);
            }
        }
        appendable.append("]]>");
        return this;
    }

    @Override
    public DefaultXMLWriter appendIndent() throws IOException {
        if (indentLength > - 1) {
            append('\n');
            for (int i = 0; i < indentLength; i++) {
                append('\t');
            }
        }
        return this;
    }

    @Override
    public DefaultXMLWriter addAttribute(String attributeName, String value) throws IOException {
        if ((value == null) || (value.length() == 0)) {
            return this;
        }
        append(' ');
        append(attributeName);
        append('=');
        append('\"');
        addText(value);
        append('\"');
        return this;
    }

    @Override
    public DefaultXMLWriter addAttribute(String attributeName, int i) throws IOException {
        append(' ');
        append(attributeName);
        append('=');
        append('\"');
        append(String.valueOf(i));
        append('\"');
        return this;
    }

}
