package net.sourceforge.htmlunit.cyberneko;

import java.io.StringReader;
import java.io.StringWriter;

import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.parser.XMLDocumentFilter;
import org.apache.xerces.xni.parser.XMLInputSource;

import junit.framework.TestCase;
import net.sourceforge.htmlunit.cyberneko.parsers.DOMParser;

/**
 * Unit tests for {@link HTMLTagBalancer}'s property {@link HTMLTagBalancer#FRAGMENT_CONTEXT_STACK}.
 *
 * @author Marc Guillemot
 */
public class FragmentContextStackTest extends TestCase {

    private static final String NL = System.lineSeparator();

    public void testSimple() throws Exception {
        final String expected = "(div" + NL
            + "(span" + NL
            + "\"hello" + NL
            + ")span" + NL
            + ")div" + NL;
        doTest("<div><span>hello</span>", new String[] { "html", "body" }, expected);

        doTest("<div><span>hello</span>", new String[] { "html" }, expected);

        doTest("<div><span>hello</span>", new String[]{}, expected);
        doTest("<div><span>hello</span>", null, expected);
    }

    public void testTR() throws Exception {
        String expected = "(tr" + NL
            + "(td" + NL
            + "\"hello" + NL
            + ")td" + NL
            + ")tr" + NL;
        doTest("<tr><td>hello</td></tr>", new String[] { "html", "body", "table", "tbody" }, expected);
        expected = "(TBODY" + NL
            + expected
            + ")TBODY\n";
        doTest("<tr><td>hello</td></tr>", new String[] { "html", "body", "table" }, expected);
        doTest("<tr><td>hello</td></tr>", new String[] { "html", "body" }, "\"hello");
    }

    public void testFragmentShouldNotCloseContextStack() throws Exception {
        final String expected = "\"helloworld\n";
        doTest("hello</div>world", new String[] { "html", "body", "div" }, expected);
        doTest("hello</span>world", new String[] { "html", "body", "div", "span" }, expected);
    }

    public void testFragmentShouldNotCloseContextStackPHack() throws Exception {
        doTest("<p>hello world</p>", new String[] { "html", "body", "p" }, ")null\r\n(p\r\n\"hello world\r\n)p");
        doTest("<p>hello world", new String[] { "html", "body", "p" }, ")null\r\n(p\r\n\"hello world\r\n)p");

        doTest("<p>hello world</p>", new String[] { "html", "body", "p", "span"}, ")null\r\n)null\r\n(p\r\n\"hello world\r\n)p");
    }

    public void testFragmentShouldNotCloseSelect() throws Exception {
        doTest("<select><option>Two</option></select>", new String[] { "html", "body", "select" },
                ")null\r\n(select\r\n(option\r\n\"Two\r\n)option\r\n)select");
    }

    private static void doTest(final String html, final String[] contextStack,
            final String expected) throws Exception {
        final DOMParser parser = new DOMParser();
        parser.setFeature("http://cyberneko.org/html/features/balance-tags/document-fragment", true);
        if (contextStack != null) {
            parser.setProperty("http://cyberneko.org/html/properties/balance-tags/fragment-context-stack", toQNames(contextStack));
        }

        final StringWriter out = new StringWriter();
        final XMLDocumentFilter[] filters = { new Writer(out) };
        parser.setProperty("http://cyberneko.org/html/properties/filters", filters);

        final StringReader sr = new StringReader(html);
        final XMLInputSource in = new XMLInputSource(null, "foo", null, sr, null);
        parser.parse(in);

        assertEquals(expected.trim(), out.toString().trim());
    }

    private static QName[] toQNames(final String[] tags) {
        final QName[] qnames = new QName[tags.length];
        for (int i = 0; i < tags.length; ++i) {
            qnames[i] = new QName(null, tags[i], null, null);
        }

        return qnames;
    }
}
@@ -2130,34 +2130,46 @@
 p%22 %7D, %22)null
-%5Cr%5Cn(p%5Cr%5Cn
+%22 + NL + %22(p%22 + NL + %22
 %5C%22hello worl
@@ -2161,36 +2161,42 @@
 + %22%5C%22hello world
-%5Cr%5Cn
+%22 + NL + %22
 )p%22);%0A        do
@@ -2258,34 +2258,46 @@
 p%22 %7D, %22)null
-%5Cr%5Cn(p%5Cr%5Cn
+%22 + NL + %22(p%22 + NL + %22
 %5C%22hello worl
@@ -2293,28 +2293,34 @@
 %22hello world
-%5Cr%5Cn
+%22 + NL + %22
 )p%22);%0A%0A     
@@ -2406,27 +2406,45 @@
 null
-%5Cr%5Cn)null%5Cr%5Cn(p%5Cr%5Cn
+%22 + NL + %22)null%22 + NL + %22(p%22 + NL + %22
 %5C%22he
@@ -2452,20 +2452,26 @@
 lo world
-%5Cr%5Cn
+%22 + NL + %22
 )p%22);%0A  
@@ -2670,54 +2670,84 @@
 null
-%5Cr%5Cn(select%5Cr%5Cn(option%5Cr%5Cn%5C%22Two%5Cr%5Cn)option%5Cr%5Cn
+%22 + NL + %22(select%22 + NL + %22(option%22 + NL + %22%5C%22Two%22 + NL + %22)option%22 + NL + %22
 )sel

fix test