Webiyo

This page was generated using Webiyo. See its source code and unit tests.

org/webiyo/xml/XmlWriter.java

package org.webiyo.xml;

import org.w3c.dom.*;

import java.io.Closeable;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class XmlWriter implements Closeable {

    private Appendable out;
    private boolean atStartOfLine = true;
    private final List<TagType> tagStack = new ArrayList<TagType>();
    private final AttributeSink atttributeSink;

    public XmlWriter(Appendable appendable) {
        out = appendable;
        atttributeSink = new AttributeSink() {
            public void putAttribute(String name, String value) throws IOException {
                appendAttribute(name, value);
            }
        };
    }

    public XmlWriter(File outputFile) throws IOException {
        this(new FileWriter(outputFile));
    }

    public void declareEntities(TagType rootTag, CharEntity... entities) throws IOException {
        out.append("<?xml version=\"1.0\"?>");
        println();

        out.append("<!DOCTYPE " + rootTag.getName() + " [\n");
        for (CharEntity entity : entities) {
            out.append("  ");
            out.append(entity.getDeclaration());
            out.append("\n");
        }
        out.append("]>");
        println();
    }

    public void startPage(DocType docType) throws IOException {
        TagType rootTag = docType.getRootTag();

        out.append("<?xml version=\"1.0\"?>");
        println();

        out.append("<!DOCTYPE " + rootTag.getName() + " PUBLIC \"");
        out.append(docType.getPublicId());
        out.append("\" \"");
        out.append(docType.getUrl());
        out.append("\">");
        println();

        startTag(rootTag);
    }

    public void startTag(TagType newTag, AttributeSource... atts) throws IOException {
        checkCanStartTag(newTag);
        tagStack.add(newTag);

        openStartTag(newTag.getName(), newTag.isOnSeparateLine());

        for (AttributeSource att : atts) {
            att.sendAttributes(atttributeSink);
        }

        out.append('>');
        atStartOfLine = false;
    }

    public void endTag(TagType tagToEnd) throws IOException {
        String tagName = tagToEnd.getName();
        if (tagStack.size() == 0) {
            throw new XmlOutputException("</" + tagName + "> doesn't match anything");
        }

        if (top() != tagToEnd) {
            throw new XmlOutputException("<" + top().getName() + "> and </" + tagName + "> don't match");
        }

        tagStack.remove(tagStack.size() - 1);
        endTag(tagName);
        if (tagToEnd.isOnSeparateLine()) {
            println();
        }
    }

    public void tag(TagType tagType, AttributeSource... atts) throws IOException {
        checkCanStartTag(tagType);

        openStartTag(tagType.getName(), tagType.isOnSeparateLine());
        for (AttributeSource att : atts) {
            att.sendAttributes(atttributeSink);
        }
        out.append("/>");
        if (tagType.isOnSeparateLine()) println();
    }

    public void text(String text) throws IOException {
        appendEscaped(text, false);
    }

    public void text(TagType tagType, String text) throws IOException {
        startTag(tagType);
        text(text);
        endTag(tagType);
    }

    public void entity(String name) throws IOException {
        out.append("&");
        out.append(name);
        out.append(";");
    }

    public void renderElement(Element element) throws IOException {
        openStartTag(element.getNodeName(), true);
        NamedNodeMap atts = element.getAttributes();
        for (int i = 0; i < atts.getLength(); i++) {
            Attr att = (Attr) atts.item(i);
            appendAttribute(att.getName(), att.getValue());
        }
        out.append('>');
        atStartOfLine = false;

        NodeList children = element.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);
            if (child instanceof Element) {
                renderElement((Element) child);
            } else if (child instanceof Text) {
                Text text = (Text) child;
                text(text.getWholeText());
            } else {
                throw new XmlOutputException("unknown node: " + child);
            }
        }

        endTag(element.getNodeName());
    }

    public void endPage() throws IOException {
        if (tagStack.size() != 1) {
            throw new XmlOutputException("endPage() doesn't match declareEntities()");
        }
        endTag(top());
    }

    public void close() throws IOException {
        if (out instanceof Closeable) {
            Closeable closable = (Closeable) out;
            out = null;
            closable.close();
        } else {
            out = null;
        }
    }
    
    // end of public methods

    private void endTag(String tagName) throws IOException {
        out.append("</");
        out.append(tagName);
        out.append('>');
    }

    private void appendEscaped(String text, boolean escapeQuotes) throws IOException {
        char[] chars = text.toCharArray();
        for (char c : chars) {
            switch (c) {
                case '<':
                    out.append("&lt;");
                    break;
                case '>':
                    out.append("&gt;");
                    break;
                case '&':
                    out.append("&amp;");
                    break;
                case '"':
                    if (escapeQuotes) {
                        out.append("&quot;");
                    } else {
                        out.append(c);
                    }
                    break;
                default:
                    out.append(c);
            }
        }
        atStartOfLine = false;
    }

    private void appendAttribute(String attName, Object attValue) throws IOException {
        out.append(' ');
        out.append(attName);
        out.append('=').append('"');
        appendEscaped(attValue.toString(), true);
        out.append('"');
    }

    private void openStartTag(String tagName, boolean separateLine) throws IOException {
        if (!atStartOfLine && separateLine) println();
        out.append('<');
        out.append(tagName);
    }

    private void checkCanStartTag(TagType newTag) throws XmlOutputException {
        if (tagStack.size() > 0) {
            if (!top().allows(newTag)) {
                throw new XmlOutputException("<" + top().getName() + "> doesn't allow <" + newTag.getName() + ">");
            }
        }
    }

    private TagType top() {
        return tagStack.get(tagStack.size() - 1);
    }

    private void println() throws IOException {
        out.append('\n');
        atStartOfLine = true;
    }

}

SourceForge