/* 
 * E-XML Library:  For XML, XML-RPC, HTTP, and related.
 * Copyright (C) 2002-2008  Elias Ross
 * 
 * genman@noderunner.net
 * http://noderunner.net/~genman
 * 
 * 1025 NE 73RD ST
 * SEATTLE WA 98115
 * USA
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * $Id$
 */

package net.noderunner.exml;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;

/**
 * This class is for unit testing the XmlScanner class.
 *
 * @author Elias Ross
 * @version 1.0
 */
public class XmlScannerTest
	extends junit.framework.TestCase
{
	public XmlScannerTest(String name) {
		super(name);
	}

	/*
	public static junit.framework.TestSuite suite() {
		return new org.hansel.CoverageDecorator(XmlScannerTest.class,
			new Class[] { XmlScanner.class });
	} 
	*/

	public static void main(String[] args) {
		junit.textui.TestRunner.run(XmlScannerTest.class);
	}

	public void testEOFCondition() 
		throws Exception
	{
		final int size = 128;
		char buf2[] = new char[size];
		StringReader sr;
		XmlScanner pr;

		String s = "abcdefghi";
		sr = new StringReader(s);
		pr = new XmlScanner(sr, size);
		assertEquals("First read", s.length(), pr.read(buf2, 0, size));
		pr.unread(buf2, 0, s.length());
		// we should be at EOF
		assertEquals("Second read", s.length(), pr.read(buf2, 0, size));
	}

	public void testReadAvailable() 
		throws Exception
	{
		final int size = 128;
		char buf2[] = new char[size];
		StringReader sr;
		XmlScanner pr;

		String s = "abcdefghi";
		sr = new StringReader(s);
		pr = new XmlScanner(sr, size);
		assertEquals(pr.read(buf2, 0, size), s.length());
		// assertEquals(0, pr.available);
		pr.unread(buf2, 0, 2);
		assertEquals(2, pr.read(buf2, 0, size));
		// assertEquals(0, pr.available);
		assertEquals('a', buf2[0]);
		assertEquals('b', buf2[1]);
	}

	public void testUnread() 
		throws Exception
	{
		final int size = 7;
		char buf2[] = new char[size];
		int got;
		StringReader sr;
		XmlScanner pr;

		String s = "ABCDEF";
		sr = new StringReader(s);
		pr = new XmlScanner(sr, size, 0);

		got = pr.read(buf2, 0, s.length() - 2);
		assertEquals(s.length() - 2, got);
		pr.unread(buf2, 0, s.length() - 2);

		got = pr.read(buf2, 0, s.length());
		assertEquals(s.length(), got);
		String s2 = new String(buf2, 0, s.length());
		assertEquals(s, s2);

		pr.unread(0);
		try {
			pr.unread(-1);
			fail("shouldn't unread -1");
		} catch (IllegalArgumentException e) {
		}
	}

	public void testFour() 
		throws Exception
	{
		final int size = 10;
		char buf2[] = new char[size];
		int got;
		StringReader sr;
		XmlScanner pr;

		sr = new StringReader("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
		pr = new XmlScanner(sr, size, 5);
		pr.unread('a');
		pr.unread('b');
		pr.unread('c');
		pr.unread('d');
		assertEquals("D", 'd', pr.read());
		assertEquals("C", 'c', pr.read());
		assertEquals("B", 'b', pr.read());
		assertEquals("A", 'a', pr.read());
		pr.unread('a');
		pr.unread('b');
		pr.read();
		pr.read();
		pr.read();
		pr = new XmlScanner(sr, size, 5);
		for (int i = 0; i < 10; i++) {
			pr.unread(new char[] {'a', 'b', 'c', 'd'}, 0, 4);
			assertEquals("PEEKED ONE", 'a', pr.peek());
			got = pr.peek(buf2, 0, 5);
			assertEquals("GOT PEEK", 5, got);
			assertEquals("PEEKED A", 'a', buf2[0]);
			assertEquals("PEEKED B", 'b', buf2[1]);
			assertEquals("PEEKED E", 'e', buf2[4]);
			// System.out.println(" " + i + " " + pr);
			got = pr.read(buf2, 0, 3);
			assertEquals("A2 " + pr, 'a', buf2[0]);
			assertEquals("B2", 'b', buf2[1]);
			assertEquals("C2", 'c', buf2[2]);
			assertEquals("D2", 'd', pr.read());
			assertEquals("E2", 'e', pr.read());
			// System.out.println(" " + i + " " + pr);
			assertEquals("E3", 'e', pr.read());
			// System.out.println(" " + i + " " + pr);
		}
	}

	public void testUnreadEasy() 
		throws Exception
	{
		final int size = 128;
		char buf2[] = new char[size];
		int got;
		StringReader sr;
		XmlScanner pr;

		String s = "abcdefghijkl";
		sr = new StringReader(s);
		pr = new XmlScanner(sr, size);

		got = pr.read(buf2, 0, size);
		assertEquals(s.length(), got);
		// assertEquals(0, pr.available);
		got = pr.read(buf2, 0, size);
		assertEquals("Should get EOF", -1, got);

		sr = new StringReader(s);
		pr = new XmlScanner(sr, size);

		got = pr.read(buf2, 0, 4);
		pr.unread(buf2, 0, 3);
		pr.read();
		got = pr.read();
		// assertEquals(1, pr.available);
		pr.unread(got);
		// assertEquals(2, pr.available);
	}

	public void testCopyUntil() 
		throws Exception
	{
		String abc = "abcdefgh";
		String s = abc + "#" + abc + "@" + abc + "#";
		StringReader sr = new StringReader(s);
		XmlScanner pr = new XmlScanner(sr, 4, 2);
		StringWriter sw;
		sw = new StringWriter(); 
		int c = pr.copyUntil(sw, '#', '?');
		assertTrue("ABC 1", sw.toString().equals(abc)); 
		assertEquals("ABC 1L", c, '#');
		assertEquals("ABC 1", '#', pr.read());
		sw = new StringWriter(); pr.copyUntil(sw, '?', '@');
		assertTrue("ABC 2 " + sw, sw.toString().equals(abc)); 
		assertEquals("ABC 2", '@', pr.read());
		sw = new StringWriter(); pr.copyUntil(sw, '#', '#');
		assertTrue("ABC 3", sw.toString().equals(abc)); 
		assertEquals("ABC 3", '#', pr.read());
		assertEquals("ABC 3", -1, pr.read());
	}

	public void testCopyUntil2() 
		throws Exception
	{
		StringWriter sw;
		int c;
		String abc = "abcdefgh";
		String s = abc + "#" + abc + "@" + abc + "#";
		StringReader sr = new StringReader(s);
		XmlScanner pr = new XmlScanner(sr, 4, 2);

		sw = new StringWriter(); 
		c = pr.copyUntil(sw, '#');
		assertEquals('#', c);
		assertTrue("ABC 1", sw.toString().equals(abc)); 
		pr.read();

		sw = new StringWriter(); 
		c = pr.copyUntil(sw, '@');
		assertEquals('@', c);
		assertEquals("ABC 1", abc, sw.toString());
	}

	public void testUnreadLots() 
		throws Exception
	{
		StringReader sr = new StringReader("");
		XmlScanner pr = new XmlScanner(sr, 1);
		pr.unread('a');
		try {
			pr.unread('b');
			fail("Can't unread another");
		} catch (IOException e) {
		}
		char buf[] = new char[64];
		assertEquals("A", 'a', pr.read());
		try {
			pr.unread(buf, 32, 2);
			fail("Can't unread 2");
		} catch (IOException e) {
		}
	}

	public void testSkipUntil() 
		throws Exception
	{
		String abc = "abcdefgh";
		String s = abc + "#" + abc + "@" + abc + "#";
		StringReader sr = new StringReader(s);
		XmlScanner pr = new XmlScanner(sr, 4, 2);
		int c = pr.skipUntil('#', '?');
		assertEquals("ABC 1L", c, '#');
		assertEquals("ABC 1", '#', pr.read());
		pr.skipUntil('?', '@');
		assertEquals("ABC 2", '@', pr.read());
		pr.skipUntil('#');
		assertEquals("ABC 3", '#', pr.read());
		assertEquals("ABC 3", -1, pr.read());
		try {
			pr.skipUntil('%');
			fail("Expected EOF");
		} catch (XmlException e) {
		}
	}

	public void testSkip() 
		throws Exception
	{
		String abc = "abcdefgh";
		String s = abc + "#" + abc + "@" + abc + "#";
		StringReader sr = new StringReader(s);
		XmlScanner pr = new XmlScanner(sr, 4, 2);
		pr.skip(abc.length());
		assertEquals("ABC 1", '#', pr.read());
		pr.skip(abc.length());
		assertEquals("ABC 2", '@', pr.read());
		pr.skip(abc.length());
		assertEquals("ABC 3", '#', pr.read());
		assertEquals("ABC 3", -1, pr.read());
		pr.skip(10);
	}

	public void testPeekMisc() 
		throws Exception
	{
		XmlScanner pr;
		pr = new XmlScanner("<");
		assertEquals("EOD", XmlEvent.EOD, pr.peekEvent());
		pr = new XmlScanner("");
		assertEquals("EOD", XmlEvent.EOD, pr.peekEvent());
	}

	public void testPeek() 
		throws Exception
	{
		String doc =
		"<?xml version='1.0' encoding='UTF-16'?>\n" +
		"<!DOCTYPE test>\n" +
		"<xml>\n" +
			"<!----->\n" +
			"<?name?????>\n" +
			"<![CDATA[]]>\n" +
			"<!ENTITY myent \"a general entity\">\n" +
			"<!ELEMENT book EMPTY>\n" +
			"<!ATTLIST book author NMTOKEN #REQURIED>\n" +
			"<![IGNORE ]]>\n" +
			"<![INCLUDE ]]>\n" +
			"stuff\n" +
			"<!NOTATION name SYSTEM \"\">\n" +
			"&amp;&apos;&quot;&lt;&gt;\n" +
		"</xml>\n" +
		"<##>\n";
		StringReader sr = new StringReader(doc);
		XmlScanner pr = new XmlScanner(sr, 16);
		assertEquals("Xml_DECL", XmlEvent.PI, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("DOCTYPE_DECL", XmlEvent.DOCTYPE_DECL, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("STAG", XmlEvent.STAG, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("COMMENT", XmlEvent.COMMENT, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("PI", XmlEvent.PI, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("CDSECT", XmlEvent.CDSECT, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("ENTITY_DECL", XmlEvent.ENTITY_DECL, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("ELEMENT_DECL", XmlEvent.ELEMENT_DECL, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("ATTLIST_DECL", XmlEvent.ATTLIST_DECL, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("CONDITIONAL_SECT", XmlEvent.CONDITIONAL_SECT, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("CONDITIONAL_SECT", XmlEvent.CONDITIONAL_SECT, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("CHARDATA", XmlEvent.CHARDATA, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("NOTATATION_DECL", XmlEvent.NOTATATION_DECL, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("REFERENCE", XmlEvent.REFERENCE, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("ETAG", XmlEvent.ETAG, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
		assertEquals("NONE", XmlEvent.NONE, pr.peekEvent());
		pr.skipUntil('\n'); pr.read();
	}

	public void testNoTranslateFakeReference()
		throws Exception
	{
		XmlScanner pr = new XmlScanner("");
		pr.setReadString("&ll;");
		assertTrue(!pr.translateReference());
		pr.setReadString("&gg;");
		assertTrue(!pr.translateReference());
		pr.setReadString("&apot;");
		assertTrue(!pr.translateReference());
		pr.setReadString("&quoo;");
		assertTrue(!pr.translateReference());
		pr.setReadString("&amm;");
		assertTrue(!pr.translateReference());
		pr.setReadString("&nbst;");
		assertTrue(!pr.translateReference());
	}

	public void testTranslateReference()
		throws Exception
	{
		StringReader sr = new StringReader("&lt;&gt;&apos;&quot;&amp;&nbsp;&;");
    // testTranslateReference(new SlowReader(sr));
		XmlScanner pr = new XmlScanner(sr, 16, 6);
		assertTrue("lt", pr.translateReference());
		assertEquals(pr.read(), '<');
		assertTrue("gt", pr.translateReference());
		assertEquals(pr.read(), '>');
		assertTrue("gt", pr.translateReference());
		assertEquals(pr.read(), '\'');
		assertTrue("quot", pr.translateReference());
		assertEquals(pr.read(), '"');
		assertTrue("amp", pr.translateReference());
		assertEquals(pr.read(), '&');
		assertTrue("nbsp", !pr.translateReference());
		pr.skip(6);
		try {
			pr.translateReference();
			fail("Not enough data in reference");
		} catch (XmlException e) {
		}
  }

	public void testReadString()
		throws Exception
	{
		XmlScanner pr = new XmlScanner("");
		pr.setReadString("abcde");
		assertEquals(pr.read(), 'a');
		assertEquals(pr.read(), 'b');
		pr.close();
		pr.setReadString("fghij");
		assertEquals(pr.read(), 'f');
		assertEquals(pr.read(), 'g');
	}

	public void testReadNS()
		throws Exception
	{
		XmlScanner pr = new XmlScanner("x a:b x c:d");
		NamespaceImpl ns = new NamespaceImpl();
		assertEquals(null, ns.getNamespaceURI());
		assertEquals(null, ns.getPrefix());
		assertEquals(null, ns.getLocalName());
		assertEquals(null, ns.getName());
		pr.readNamespace(ns);
		assertEquals(null, ns.getNamespaceURI());
		assertEquals(null, ns.getPrefix());
		assertEquals(null, ns.getLocalName());
		assertEquals("x", ns.getName());
		pr.read();
		pr.readNamespace(ns);
		assertEquals(null, ns.getNamespaceURI());
		assertEquals("a", ns.getPrefix());
		assertEquals("b", ns.getLocalName());
		assertEquals("a:b", ns.getName());
		pr.read();
		pr.readNamespace(ns);
		assertEquals(null, ns.getNamespaceURI());
		assertEquals(null, ns.getPrefix());
		assertEquals(null, ns.getLocalName());
		assertEquals("x", ns.getName());
		pr.read();
		pr.readNamespace(ns);
		assertEquals(null, ns.getNamespaceURI());
		assertEquals("c", ns.getPrefix());
		assertEquals("d", ns.getLocalName());
		assertEquals("c:d", ns.getName());
		pr.readNamespace(ns); // EOF shouldn't matter
		assertEquals(null, ns.getNamespaceURI());
		assertEquals(null, ns.getPrefix());
		assertEquals(null, ns.getLocalName());
		assertEquals(null, ns.getName());
	}

	public void testReadNS2()
		throws Exception
	{
		XmlScanner pr;
		NamespaceImpl ns;
		pr = new XmlScanner("a:");
		ns = new NamespaceImpl();
		try {
			pr.readNamespace(ns);
			fail("bad namespace");
		} catch (XmlException e) { }
		pr = new XmlScanner(":b");
		ns = new NamespaceImpl();
		try {
			pr.readNamespace(ns);
			fail("bad namespace");
		} catch (XmlException e) { }
		pr = new XmlScanner(":");
		try {
			pr.readNamespace(ns);
			fail("bad namespace");
		} catch (XmlException e) { }
		pr = new XmlScanner("");
		pr.readNamespace(ns);
		assertEquals(null, ns.getNamespaceURI());
		assertEquals(null, ns.getPrefix());
		assertEquals(null, ns.getLocalName());
		assertEquals(null, ns.getName());
	}

	public void testCanUnread()
		throws Exception
	{
		XmlScanner pr;
		pr = new XmlScanner("abc");
		pr.read(); 
		pr.read(); 
		pr.read(); 
		assertEquals(3, pr.canUnread()); 
		pr.unread("xyz");
		assertEquals('x', pr.read()); 
	}

	public void testCharRef()
		throws Exception
	{
		XmlScanner pr;
		pr = new XmlScanner("&#13;");
		assertTrue(pr.charRef());
		assertEquals('\r', pr.read()); 

		pr = new XmlScanner("&#x0d;");
		assertTrue(pr.charRef());
		assertEquals('\r', pr.read()); 

		pr = new XmlScanner("");
		try {
			pr.charRef();
			fail("EOF");
		} catch (XmlException e) { }

		pr = new XmlScanner("&#xe");
		try {
			pr.charRef();
			fail("EOF in ref");
		} catch (XmlException e) { }


		pr = new XmlScanner("xyz");
		assertTrue("not here", !pr.charRef());
		pr = new XmlScanner("&xyz;");
		assertTrue("not here", !pr.charRef());

		pr = new XmlScanner("&#00;");
		try {
			pr.charRef();
			fail("bad char");
		} catch (XmlException e) { }
	}

	public void testSetReader()
		throws Exception
	{
		XmlScanner pr;
		pr = new XmlScanner("abc");
		assertEquals('a', pr.read()); 
		pr.setReader(new StringReader("xyz"));
		assertEquals('x', pr.read()); 
	}

	public void testPeekEOF()
		throws Exception
	{
		XmlScanner pr;
		pr = new XmlScanner("");
		int c = pr.peek(new char[10], 0, 10);
		assertEquals(-1, c);
	}

	public void testCopyUntilEOF()
		throws Exception
	{
		XmlScanner pr;
		pr = new XmlScanner("");
		StringWriter sw = new StringWriter();
		pr.copyUntil(sw, 'a', 'b');
		assertEquals(0, sw.toString().length());
	}

	public void testGetLongness()
		throws Exception
	{
		XmlScanner pr;
		StringBuffer sb = new StringBuffer(1024);
		for (int i = 0; i < 2048; i++)
			sb.append('a');
		pr = new XmlScanner(new StringReader(sb.toString()), 16);
		try {
			pr.getName();
			fail("long name");
		} catch (XmlException e) {
		}
		pr = new XmlScanner(new StringReader("&#x" + sb + ";"), 16);
		try {
			pr.charRef();
			fail("long charRef name");
		} catch (XmlException e) {
		}
	}

	public void testGetNameSlow()
		throws Exception
	{
		XmlScanner pr;
		String a = "abcde";
		pr = new XmlScanner(new StringReader(a + "||"), 2, 0);
		pr.getStringPool().add(a);
		String name = pr.getName();
		assertEquals(a, name);
		assertTrue("object eq", a == name);
	}

	public void testGetNameSP()
		throws Exception
	{
		XmlScanner pr;
		pr = new XmlScanner("123");
		assertNull(pr.getName());

		pr = new XmlScanner("A:A ");
		assertEquals("equal", "A:A", pr.getName());

		String A = "A";
		StringPool sp = new StringPool();
		sp.add(A);
		pr = new XmlScanner("A");
		pr.setStringPool(sp);
		assertEquals(sp, pr.getStringPool());

		String name = pr.getName();
		assertEquals("equal", A, name);
		assertTrue("object equal", A == name);
	}

	public void testUriMap()
		throws Exception
	{
		XmlScanner pr = new XmlScanner("a:b c:d e");
		NamespaceImpl ns = new NamespaceImpl();
		assertTrue("not null", null != pr.getUriMap());
		UriMap uriMap = new UriMap();
		pr.setUriMap(uriMap);
		assertEquals(uriMap, pr.getUriMap());
		String URI_1 = "http://example.net/a";
		String URI_2 = "http://example.net/c";
		uriMap.put("a", URI_1);
		uriMap.put("c", URI_2);

		pr.readNamespace(ns);
		assertEquals(URI_1, ns.getNamespaceURI());
		assertEquals("a", ns.getPrefix());
		assertEquals("b", ns.getLocalName());
		assertEquals("a:b", ns.getName());
		ns.toString(); // test ..
		pr.read();

		pr.readNamespace(ns);
		assertEquals(URI_2, ns.getNamespaceURI());
		assertEquals("c", ns.getPrefix());
		assertEquals("d", ns.getLocalName());
		assertEquals("c:d", ns.getName());
		ns.toString(); // test ..
		pr.read();

		pr.readNamespace(ns);
		assertEquals(null, ns.getNamespaceURI());
		assertEquals(null, ns.getPrefix());
		assertEquals(null, ns.getLocalName());
		assertEquals("e", ns.getName());
	}

  private class SlowReader extends Reader {
    Reader r;
    public SlowReader(Reader r) {
      this.r = r;
    }
    public int read(char[] cbuf, int off, int len) throws IOException {
      return r.read(cbuf, off, 1);
    }
    public void close() throws IOException {
      r.close();
    }
  }

	public void testCanonicalCharData()
		throws Exception
	{
		XmlScanner pr;

    pr = new XmlScanner("<");
    assertEquals("", pr.getCanonicalText());
    pr = new XmlScanner("&fudge;");
    assertEquals("", pr.getCanonicalText());
		pr = new XmlScanner("&amp;");
    assertEquals("&", pr.getCanonicalText());
		pr = new XmlScanner("&amp;amp;");
    assertEquals("&amp;", pr.getCanonicalText());
		pr = new XmlScanner("&lt;");
    assertEquals("<", pr.getCanonicalText());
		pr = new XmlScanner("&fudge;");
    assertEquals("", pr.getCanonicalText());
		pr = new XmlScanner("apple");
    assertEquals("apple", pr.getCanonicalText());
		pr = new XmlScanner("apple<sauce");
    assertEquals("apple", pr.getCanonicalText());
		pr = new XmlScanner("apple&lt;sauce");
    assertEquals("apple<sauce", pr.getCanonicalText());
		pr = new XmlScanner("apple&amp;sauce");
    assertEquals("apple&sauce", pr.getCanonicalText());

    String A = "apple";
    pr.getStringPool().intern(A);
    pr.setReadString("apple<>ly");
    assertTrue("apple" == pr.getCanonicalText());
    pr.setReadString("&amp;apple");
    pr.read();

    pr.setReadString("banana");
    String B = pr.getCanonicalText();
    pr.setReadString("banana&fud;ge");
    assertTrue(B == pr.getCanonicalText());
    pr.setReader(new SlowReader(new StringReader("banana")));
    assertTrue(B == pr.getCanonicalText());
    pr.setReader(new SlowReader(new StringReader("banana<")));
    assertTrue(B == pr.getCanonicalText());
  }
}
