package wstream;

import java.io.*;

import javax.xml.stream.*;

import org.codehaus.stax2.*;

/**
 * These tests try to specifically verify that name encoding and decoding
 * work as expected. Although basic Stax or Stax2 test should cover
 * some aspects, they may not force exact stream reader implementations
 * to use and thus might not uncover all problems Aalto may have.
 */
public class TestNameEncoding
    extends base.BaseTestCase
{
    // // // First, valid names for different classes of encodings

    final static String NAME_ASCII = "_a123";
    // "A" and second "c" of "Accents" with some decorations...
    final static String NAME_LATIN1 = "\u00C0"+"c\u00C7ents_";
    final static String NAME_UTF8 = "\u0c12\u114c_x";

    // // // And then similarly invalid ones
    // // // (note: ensured to be invalid in xml 1.1, too, to minimize
    // // // problems if and when parser supports 1.1)

    final static String INVALID_NAME_ASCII = "a b"; // no spaces
    final static String INVALID_NAME2_ASCII = "a:b"; // no no spaces
    final static String INVALID_NAME_LATIN1 = "abc\u00BEx"; // ctrl-char
    final static String INVALID_NAME_UTF8 = "+a\u2ff0";

    public void testValidElemNameEncodingUtf8()
        throws Exception
    {
        verifyValidElemName(NAME_ASCII, ENC_UTF8);
        verifyValidElemName(NAME_LATIN1, ENC_UTF8);
        verifyValidElemName(NAME_UTF8, ENC_UTF8);
    }

    public void testValidElemNameEncodingLatin1()
        throws Exception
    {
        verifyValidElemName(NAME_ASCII, ENC_LATIN1);
        verifyValidElemName(NAME_LATIN1, ENC_LATIN1);
    }

    public void testValidElemNameEncodingAscii()
        throws Exception
    {
        verifyValidElemName(NAME_ASCII, ENC_ASCII);
    }

    // // // Then tests for invalid name chars

    public void testInvalidElemNameEncodingUtf8()
        throws Exception
    {
        /* With UTF-8, only invalidity itself matters, as
         * all legal Unicode chars can be encoded
         */
        verifyInvalidElemName(INVALID_NAME_ASCII, ENC_UTF8);
        verifyInvalidElemName(INVALID_NAME_LATIN1, ENC_UTF8);
        verifyInvalidElemName(INVALID_NAME_UTF8, ENC_UTF8);
    }

    public void testInvalidElemNameEncodingLatin1()
        throws Exception
    {
        /* Validity checks should fail for Latin1/Ascii expressable Strings
         * that contain non-name chars:
         */
        verifyInvalidElemName(INVALID_NAME_ASCII, ENC_LATIN1);
        verifyInvalidElemName(INVALID_NAME_LATIN1, ENC_LATIN1);

        /* With Latin1, chars beyond 8-bit range can't be encoded;
         * hence otherwise valid UTF-8 name should fail
         */
        verifyInvalidElemName(NAME_UTF8, ENC_LATIN1);
    }

    public void testInvalidElemNameEncodingAscii()
        throws Exception
    {
        /* Validity checks should fail for Ascii expressable Strings
         * that contain non-name chars:
         */
        verifyInvalidElemName(INVALID_NAME_ASCII, ENC_ASCII);

        /* With Ascii, chars beyond 8-bit range can't be encoded;
         * hence otherwise valid Latin1/UTF-8 name should fail
         */
        verifyInvalidElemName(NAME_LATIN1, ENC_ASCII);
        verifyInvalidElemName(NAME_UTF8, ENC_ASCII);
    }

    /*
    ////////////////////////////////////////////////////////
    // Helper methods
    ////////////////////////////////////////////////////////
     */

    void verifyValidElemName(String name, String enc)
        throws IOException, XMLStreamException
    {
        verifyValidElemName(writeSimpleDoc(name, enc), name);
    }

    void verifyInvalidElemName(String name, String enc)
        throws IOException, XMLStreamException
    {
        try {
            writeSimpleDoc(name, enc);
            fail("Expected exception for invalid element name ("+name+"), encoding "+enc);
        } catch (XMLStreamException xse) {
            String msg = xse.getMessage();
            String exp1 = "invalid name";
            String exp2 = "illegal name";

            String lmsg = msg.toLowerCase();
            if (lmsg.indexOf(exp1) < 0 && lmsg.indexOf(exp2) < 0) {
                fail("Received exception for name \""+name+"\", encoding enc; but message did not contain fragment '"+exp1+"' or fragment '"+exp2+"'. Instead was: "+msg);
            }
        }
    }

    byte[] writeSimpleDoc(String name, String enc)
        throws IOException, XMLStreamException
    {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        XMLStreamWriter sw = newOutputFactory().createXMLStreamWriter(bos, enc);
        sw.writeStartDocument(enc, "1.0");
        sw.writeStartElement(name);
        sw.writeEndElement();
        sw.writeEndDocument();
        sw.close();
        bos.close();
        return bos.toByteArray();
    }

    void verifyValidElemName(byte[] content, String name)
        throws IOException, XMLStreamException
    {
        // note: must specify encoding so parsers knows which decoder to use
        XMLStreamReader sr = createReader(content, name);
        assertTokenType(START_DOCUMENT, sr.getEventType());
        assertTokenType(START_ELEMENT, sr.next());
        assertEquals(name, sr.getLocalName());
        assertTokenType(END_ELEMENT, sr.next());
        assertEquals(name, sr.getLocalName());
        assertTokenType(END_DOCUMENT, sr.next());
        sr.close();
    }

    XMLStreamReader createReader(byte[] content, String enc)
        throws IOException, XMLStreamException
    {
        // Let's ensure it's a new factory, to minimize caching probs
        XMLInputFactory2 f = newInputFactory();
        return f.createXMLStreamReader(new ByteArrayInputStream(content));
    }
}

