/*
 * Copyright (c) 2006, John Kristian
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *      *   Redistributions of source code must retain the above copyright
 *          notice, this list of conditions and the following disclaimer.
 *
 *      *   Redistributions in binary form must reproduce the above copyright
 *          notice, this list of conditions and the following disclaimer in the
 *          documentation and/or other materials provided with the distribution.
 *
 *      *   Neither the name of StAX-Utils nor the names of its contributors
 *          may be used to endorse or promote products derived from this
 *          software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
package javanet.staxutils;

import java.io.StringWriter;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

/**
 * Test IndentingXMLEventWriter and IndentingXMLStreamWriter. Those two classes
 * are expected to output identical XML, in all test cases.
 * 
 * @author <a href="mailto:jk2006@engineer.com">John Kristian</a>
 */
public class IndentingTest extends TestCase {

    /** Each test case is represented by an object. */
    private static Case[] newCases() {
        return new Case[] //
        { new Case("empty document") {
            protected void test() throws Exception {
                String expected = "";
                String actual = getActual();
                assertEquals(expected, actual);
            }
        }, new Case("empty element") {
            protected void test() throws Exception {
                stream.writeEmptyElement("alpha");
                String expected = "<alpha />";
                String actual = getActual();
                assertEquals(expected, actual);
            }
        }, new Case("element in document") {
            protected void test() throws Exception {
                stream.writeStartDocument();
                stream.writeStartElement("alpha");
                stream.writeEndElement();
                stream.writeEndDocument();
                String expected = DECLARATION //
                        + NL + "<alpha />" //
                        + NL;
                String actual = getActual();
                if (!expected.equalsIgnoreCase(actual)) {
                    assertEquals(expected, actual);
                }
            }
        }, new Case("nested elements; data-oriented; omit writeEndElement") {
            protected void test() throws Exception {
                stream.writeStartDocument();
                stream.writeStartElement("alpha");
                stream.writeStartElement("bravo");
                stream.writeStartElement("charlie");
                stream.writeStartElement("delta");
                stream.writeStartElement("echo");
                stream.writeEndElement(); // echo
                stream.writeEndElement(); // delta
                stream.writeStartElement("delta");
                stream.writeCharacters("data");
                stream.writeEndDocument();
                String expected = DECLARATION //
                        + NL + "<alpha>" //
                        + NL + "  <bravo>" //
                        + NL + "    <charlie>" //
                        + NL + "      <delta>" //
                        + NL + "        <echo />" //
                        + NL + "      </delta>" //
                        + NL + "      <delta>data</delta>" //
                        + NL + "    </charlie>" //
                        + NL + "  </bravo>" //
                        + NL + "</alpha>" //
                        + NL;
                String actual = getActual();
                if (!expected.equalsIgnoreCase(actual)) {
                    assertEquals(expected, actual);
                }
            }
        }, new Case("DTD; mixed content; omit writeEndElement") {
            protected void test() throws Exception {
                stream.writeStartDocument();
                stream.writeDTD("a DTD");
                stream.writeStartElement("alpha");
                stream.writeCharacters("data");
                stream.writeStartElement("bravo");
                stream.writeCharacters("data");
                stream.writeEndDocument();
                String expected = DECLARATION //
                        + NL + "a DTD" //
                        + NL + "<alpha>data<bravo>data</bravo></alpha>" //
                        + NL;
                String actual = getActual();
                if (!expected.equalsIgnoreCase(actual)) {
                    assertEquals(expected, actual);
                }
            }
        }, new Case("bizarre punctuation; processing instruction; comment") {
            protected void test() throws Exception {
                indenter.setNewLine(indenter.getNewLine() + "+");
                indenter.setIndent(".");
                stream.writeStartDocument();
                stream.writeStartElement("jokes");
                stream.writeProcessingInstruction("target", "data");
                stream.writeStartElement("riddle");
                final String Q = "Why did the chicken cross the road?";
                final String A = "To get to the other side.";
                stream.writeComment(Q);
                stream.writeStartElement("answer");
                stream.writeCharacters(A);
                stream.writeEndDocument();
                String expected = DECLARATION + "<jokes>" //
                        + NL + "+.<?target data?>" //
                        + NL + "+.<riddle>" //
                        + NL + "+..<!--" + Q + "-->" //
                        + NL + "+..<answer>" + A + "</answer>" //
                        + NL + "+.</riddle>" //
                        + NL + "+</jokes>";
                String actual = getActual();
                if (!expected.equalsIgnoreCase(actual)) {
                    assertEquals(expected, actual);
                }
            }
        }, new Case("erroneous usage") {
            protected void test() throws Exception {
                try {
                    stream.writeCharacters("data");
                    fail();
                } catch (XMLStreamException expected) {
                } catch (IllegalStateException actual) {
                }
                try {
                    stream.writeEndElement();
                    fail();
                } catch (XMLStreamException expected) {
                }
                stream.writeStartElement("x");
                try {
                    stream.writeDTD("DTD");
                    fail();
                } catch (XMLStreamException expected) {
                }
                try {
                    stream.writeStartDocument();
                    fail();
                } catch (XMLStreamException expected) {
                } catch (IllegalStateException actual) {
                }
                stream.writeEndDocument();
                try {
                    stream.writeStartDocument();
                    fail();
                } catch (XMLStreamException expected) {
                } catch (IllegalStateException actual) {
                }
                try {
                    stream.writeDTD("DTD");
                    fail();
                } catch (XMLStreamException expected) {
                } catch (IllegalStateException actual) {
                }
                try {
                    stream.writeStartElement("y");
                    fail();
                } catch (XMLStreamException expected) {
                } catch (IllegalStateException actual) {
                }
                try {
                    stream.writeCharacters(null);
                    fail();
                } catch (XMLStreamException expected) {
                } catch (NullPointerException actual) {
                }
                stream.writeProcessingInstruction("target");
                stream.writeComment("comment");
                String expected = "<x>" //
                        + NL + "  " // before DTD
                        + NL + "  " // before StartDocument
                        + NL + "</x>" //
                        + NL + "<?target?>" //
                        + NL + "<!--comment-->";
                String actual = getActual();
                assertEquals(expected, actual);
            }
        } };
    }

    private static abstract class Case extends TestCase {

        protected Case(String name) {
            super(name);
        }

        protected abstract void test() throws Exception;

        protected Indentation indenter;

        protected XMLStreamWriter stream;

        protected static final String DECLARATION = "<?xml version='1.0' encoding='UTF-8'?>";

        protected static final String NL = "\n";

        protected String getActual() throws Exception {
            stream.flush();
            writer.flush();
            return writer.toString();
        }

        private StringWriter writer = new StringWriter();

        public void runBare() throws Throwable {
            test();
        }

        public String getName() {
            return prefix + super.getName();
        }

        private String prefix;

    }

    /**
     * Construct a TestSuite that applies each test case to an
     * IndentingXMLStreamWriter and an IndentingXMLEventWriter.
     */
    public static Test suite() throws Exception {
        TestSuite s = new TestSuite();
        XMLOutputFactory outputFactory = new StaxUtilsXMLOutputFactory();
        outputFactory.setProperty(StaxUtilsXMLOutputFactory.INDENTING, Boolean.TRUE);
        Case[] cases = newCases();
        for (int c = 0; c < cases.length; ++c) {
            Case thisCase = cases[c];
            XMLStreamWriter subject = outputFactory.createXMLStreamWriter(thisCase.writer);
            thisCase.indenter = (Indentation) subject;
            thisCase.stream = subject;
            thisCase.prefix = "Stream ";
            s.addTest(thisCase);
        }
        cases = newCases();
        for (int c = 0; c < cases.length; ++c) {
            Case thisCase = cases[c];
            XMLEventWriter subject = outputFactory.createXMLEventWriter(thisCase.writer);
            thisCase.indenter = (Indentation) subject;
            thisCase.stream = new XMLEventStreamWriter(subject);
            thisCase.prefix = "Event ";
            s.addTest(thisCase);
        }
        assertEquals("factory.indent", Boolean.TRUE, outputFactory
                .getProperty(StaxUtilsXMLOutputFactory.INDENTING));
        return s;
    }

}
