package stax2.stream;

import java.io.*;
import java.util.*;

import javax.xml.stream.*;

import stax2.BaseStax2Test;

/**
 * Unit test(s) that verify correct functioning of
 * {@link XMLStreamReader#getElementText}. This might actually
 * belong more to the core StaxTest, but as bug was found from
 * Woodstox, let's start by just adding them here first.
 *
 * @author Tatu Saloranta
 *
 * @since 3.0
 */
public class TestGetElement
    extends BaseStax2Test
{
    public void testLargeDocCoalesce() throws XMLStreamException
    {
        _testLargeDoc(true);
    }

    public void testLargeDocNonCoalesce() throws XMLStreamException
    {
        _testLargeDoc(false);
    }

    public void testLongSegmentCoalesce() throws XMLStreamException
    {
        _testLongSegment(true);
    }

    public void testLongSegmentNonCoalesce() throws XMLStreamException
    {
        _testLongSegment(false);
    }

    /*
    ///////////////////////////////////////////////
    // Second level test methods
    ///////////////////////////////////////////////
     */

    private void _testLargeDoc(boolean coalesce)
        throws XMLStreamException
    {
        final int LEN = 258000;
        final long SEED = 72;

        ByteArrayOutputStream bos = new ByteArrayOutputStream(LEN+2000);
        int rowCount = generateDoc(SEED, LEN, bos);
        XMLInputFactory f = getInputFactory();
        byte[] docData = bos.toByteArray();

        // Let's test both coalescing and non-coalescing:
        setCoalescing(f, coalesce);
        XMLStreamReader sr = f.createXMLStreamReader(new ByteArrayInputStream(docData));
        assertTokenType(START_ELEMENT, sr.next());
        assertEquals("data", sr.getLocalName());
        int actRowCount = 0;
        Random r = new Random(SEED);
        
        while (sr.nextTag() == START_ELEMENT) { // <row>
            ++actRowCount;
            assertEquals("row", sr.getLocalName());
            expectElemText(sr, "a", String.valueOf(r.nextInt()));
            expectElemText(sr, "b", String.valueOf(r.nextLong()));
            expectElemText(sr, "c", String.valueOf(r.nextBoolean()));
            assertTokenType(END_ELEMENT, sr.nextTag()); // match </row>
        }
        assertEquals(rowCount, actRowCount);
    }

    private void _testLongSegment(boolean coalesce)
        throws XMLStreamException
    {
        final int LEN = 129000;
        Random r = new Random(17);

        StringBuilder sb = new StringBuilder(LEN + 2000);
        while (sb.length() < LEN) {
            switch (r.nextInt() & 7) {
            case 0:
                sb.append("123");
                break;
            case 1:
                sb.append("foo\nbar");
                break;
            case 2:
                sb.append("rock & roll!");
                break;
            default:
                sb.append(sb.length());
                break;
            }
        }

        String contentStr = sb.toString();
        StringWriter strw = new StringWriter(LEN + 4000);
        XMLStreamWriter sw = getOutputFactory().createXMLStreamWriter(strw);

        sw.writeStartDocument();
        sw.writeStartElement("data");
        sw.writeCharacters(contentStr);
        sw.writeEndElement();
        sw.writeEndDocument();
        sw.close();
 
        XMLInputFactory f = getInputFactory();
        setCoalescing(f, coalesce);
        XMLStreamReader sr = f.createXMLStreamReader(new StringReader(strw.toString()));
        assertTokenType(START_ELEMENT, sr.next());
        assertEquals("data", sr.getLocalName());
        String actStr = sr.getElementText();
        assertEquals(contentStr, actStr);
        assertTokenType(END_ELEMENT, sr.getEventType());
        assertTokenType(END_DOCUMENT, sr.next());
    }

    public void testGetMixedContentElementText() throws Exception
    {
        final String XML = "<root>foo<![CDATA[bar]]></root>";

        XMLInputFactory ifact = getInputFactory();
        XMLStreamReader sr;

        // Start with non-coalescing case
        setCoalescing(ifact, false);
        sr = ifact.createXMLStreamReader(new StringReader(XML));
        assertTokenType(START_ELEMENT, sr.next());
        assertEquals("foobar", sr.getElementText());
        assertTokenType(END_ELEMENT, sr.getEventType());
        assertTokenType(END_DOCUMENT, sr.next());
        sr.close();

        // and then with coalescing
        setCoalescing(ifact, true);
        sr = ifact.createXMLStreamReader(new StringReader(XML));
        assertTokenType(START_ELEMENT, sr.next());
        assertEquals("foobar", sr.getElementText());
        assertTokenType(END_ELEMENT, sr.getEventType());
        assertTokenType(END_DOCUMENT, sr.next());
        sr.close();
    }

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

    private int generateDoc(long SEED, int LEN, ByteArrayOutputStream out)
        throws XMLStreamException
    {
        XMLStreamWriter sw = getOutputFactory().createXMLStreamWriter(out, "UTF-8");
        Random r = new Random(SEED);

        sw.writeStartDocument();
        sw.writeStartElement("data");

        int rowCount = 0;

        while (out.size() < LEN) {
            sw.writeStartElement("row");

            sw.writeStartElement("a");
            sw.writeCharacters(String.valueOf(r.nextInt()));
            sw.writeEndElement();
            sw.writeStartElement("b");
            sw.writeCharacters(String.valueOf(r.nextLong()));
            sw.writeEndElement();
            sw.writeStartElement("c");
            sw.writeCharacters(String.valueOf(r.nextBoolean()));
            sw.writeEndElement();
            sw.writeCharacters("\n"); // to make debugging easier

            sw.writeEndElement();
            sw.flush();
            ++rowCount;
        }
        sw.writeEndElement();
        sw.writeEndDocument();
        sw.close();
        return rowCount;
    }

    private void expectElemText(XMLStreamReader sr, String elem, String value)
        throws XMLStreamException
    {
        assertTokenType(START_ELEMENT, sr.nextTag());
        assertEquals(elem, sr.getLocalName());
        String actValue = sr.getElementText();
        if (!value.equals(actValue)) {
            fail("Expected value '"+value+"' (for element '"+elem+"'), got '"+actValue+"' (len "+actValue.length()+"): location "+sr.getLocation());
        }
        assertTokenType(END_ELEMENT, sr.getEventType());
    }
}
