/* 
 * 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.BufferedWriter;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;

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

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

	static String testDoc =
	"<?xml version='1.0' encoding='UTF-16'?>\n" +
	"<!DOCTYPE test>\n" +
	"<xml>\n" +
		"<empty attr='/'/>\n" +
		"<!--comment-here-->\n" +
		"<!---->\n" +
		"<?name?????>\n" +
		"<![CDATA[]]>\n" +
		"<![CDATA[ section ]]]]>\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" +
		"<a></a>\n" +
	"</xml>\n";

	// don't ignore whitespace and repeat text events
	static int testDocEvents2[] = {
		XmlEvent.XML_DECL, XmlEvent.CHARDATA,
		XmlEvent.DOCTYPE_DECL, XmlEvent.CHARDATA,
		XmlEvent.STAG, XmlEvent.CHARDATA,
		XmlEvent.STAG, XmlEvent.ETAG, XmlEvent.CHARDATA,
		XmlEvent.COMMENT, XmlEvent.CHARDATA,
		XmlEvent.COMMENT, XmlEvent.CHARDATA,
		XmlEvent.PI, XmlEvent.CHARDATA,
		XmlEvent.CDSECT, XmlEvent.CHARDATA,
		XmlEvent.CDSECT, XmlEvent.CHARDATA,
		XmlEvent.ENTITY_DECL, XmlEvent.CHARDATA,
		XmlEvent.ELEMENT_DECL, XmlEvent.CHARDATA,
		XmlEvent.ATTLIST_DECL, XmlEvent.CHARDATA,
		XmlEvent.CONDITIONAL_SECT, XmlEvent.CHARDATA,
		XmlEvent.CONDITIONAL_SECT, XmlEvent.CHARDATA,
		XmlEvent.NOTATATION_DECL, XmlEvent.CHARDATA,
		XmlEvent.REFERENCE, XmlEvent.REFERENCE,
		XmlEvent.REFERENCE, XmlEvent.REFERENCE,
		XmlEvent.REFERENCE, XmlEvent.CHARDATA,
		XmlEvent.STAG, XmlEvent.ETAG,
		XmlEvent.CHARDATA, XmlEvent.ETAG,
		XmlEvent.EOD
	};

	static int testDocEvents[] = {
		XmlEvent.XML_DECL, // 0
		XmlEvent.DOCTYPE_DECL, // 1
		XmlEvent.STAG, // 2
		XmlEvent.STAG, XmlEvent.ETAG, // 3
		XmlEvent.COMMENT, // 4
		XmlEvent.COMMENT, // 5
		XmlEvent.PI, // 6
		XmlEvent.CDSECT, // 7
		// XmlEvent.CDSECT,
		XmlEvent.ENTITY_DECL, // 8
		XmlEvent.ELEMENT_DECL, // 9
		XmlEvent.ATTLIST_DECL, // 10
		XmlEvent.CONDITIONAL_SECT, // 11
		XmlEvent.CONDITIONAL_SECT, // 12
		XmlEvent.CHARDATA, // 13
		XmlEvent.NOTATATION_DECL, // 14
		XmlEvent.REFERENCE, // 15
		// XmlEvent.REFERENCE,
		// XmlEvent.REFERENCE,
		// XmlEvent.REFERENCE,
		// XmlEvent.REFERENCE,
		XmlEvent.STAG, XmlEvent.ETAG,
		XmlEvent.ETAG,
		XmlEvent.EOD
	};


	public void testEventStream() 
		throws Exception
	{
		XmlSParser parser;
		Reader r;
		r = new StringReader(testDoc);
		parser = new XmlSParser(new XmlReader(r), XmlEvent.ALL_EVENTS);
		int i = 0;
		while (parser.hasNext()) {
			int event = parser.next();
			assertTrue("not match " + parser, !parser.matches("XXX".toCharArray()));
			assertEquals("Expect event " + i, testDocEvents[i++], event);
		}
	}

	public void testEventStreamReset() 
		throws Exception
	{
		XmlSParser parser;
		parser = new XmlSParser(new XmlReader(), XmlEvent.ALL_EVENTS);
		for (int j = 0; j < 2; j++) {
			parser.setReadString(testDoc);
			for (int i = 0; i < testDocEvents.length - 4; i++) {
				int event = parser.next();
				assertEquals("Expect event " + i, testDocEvents[i], event);
			}
		}
	}

	public void testEventStreamNoSkip() 
		throws Exception
	{
		XmlSParser parser;
		Reader r;
		r = new StringReader(testDoc);
		parser = new XmlSParser(new XmlReader(r), XmlEvent.ALL_EVENTS);
		parser.setSkipWS(false);
		parser.setSkipRepeatTextEvents(false);
		int i = 0;
		while (parser.hasNext()) {
			int event = parser.next();
			assertEquals("Expect event " + i, testDocEvents2[i++], event);
		}
	}

	public void testFilteredEventStream() 
		throws Exception
	{
		XmlSParser parser;
		Reader r;

		for (int i = 0; i < 14; i++) { 
			int found = 0;
			int retEvent = 1<<i;

			r = new StringReader(testDoc);
			parser = new XmlSParser(new XmlReader(r), XmlEvent.EOD | retEvent);
			while (parser.hasNext()) {
				int event = parser.next();
				if (!parser.isEvent(XmlEvent.EOD)) {
					assertEquals("Expect event " + XmlSParser.eventToString(event), 
						retEvent, event);
					assertTrue("Expect event " + XmlSParser.eventToString(event), 
						parser.isEvent(retEvent));
					found++;
				}
			}
			assertTrue("hasNext false", !parser.hasNext());
			if (retEvent != XmlEvent.EOD)
				assertTrue("Expect an event " + XmlSParser.eventToString(retEvent), found > 0);
		}
	}

	public static void main(String[] args) {
		if (args.length > 0) {
			try {
				for (int i = 0; i < args.length; i++) {
					InputStream fis = new FileInputStream(args[i]);
					Reader r = new InputStreamReader(fis);
					XmlSParser parser;
					parser = new XmlSParser(new XmlReader(r), XmlEvent.ALL_EVENTS);
					parser.setSkipWS(false);
					parser.setSkipRepeatTextEvents(false);
					int event;
					while (parser.hasNext()) {
						event = parser.next();
						System.out.println("Event " + XmlSParser.eventToString(event));
						if (event == XmlEvent.TEXT) {
							System.out.println("Got text " + parser.getText().trim());
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		junit.textui.TestRunner.run(XmlSParserTest.class);
	}

	public void testNoContent() 
		throws Exception
	{
		XmlSParser parser;
		Reader r;
		r = new StringReader("");
		parser = new XmlSParser(new XmlReader(r));
		assertTrue("no data", parser.isEvent(XmlEvent.NONE));
	}

	public static final String D_CONTENT = "D's content here";

	/**
	 * Create a new parser for testing.  Also checks that XmlWriter is
	 * creating valid Xml.
	 */
	public static XmlSParser newParser() 
		throws Exception
	{
		XmlWriter writer;
		StringWriter sw = new StringWriter();
		writer = new XmlWriter(new BufferedWriter(sw));
		writer.writeHeader("UTF-8");
		Element root = new Element("test");
		Element a = new Element("a");
		Element b = new Element("b");
		Element b2 = new Element("b2");
		Element c = new Element("c");
		Element d = new Element("d");
		writer.startElement(root);
			writer.startElement(a);
				writer.startElement(b);
					writer.emptyElement(c);
					writer.startElement(d);
						writer.writeCData(D_CONTENT);
					writer.endElement();
				writer.endElement();
				writer.startElement(b2);
				writer.endElement();
			writer.endElement();
		writer.endElement();
		writer.flush();
		String in = sw.getBuffer().toString();

		ElementRule rule;

		rule = new ElementRule();
		Dtd dtd = new Dtd();
		dtd.addElementRule(root, rule);
		dtd.addElementRule(a, rule);
		dtd.addElementRule(b, rule);
		dtd.addElementRule(b2, rule);
		dtd.addElementRule(c, rule);
		rule = new ElementRule();
		rule.setAllowPCData(true);
		dtd.addElementRule(d, rule);

		XmlReader reader = new XmlReader(new StringReader(in), dtd);

		return new XmlSParser(reader);
	}

	public void testBasicDoc()
		throws Exception
	{
		Dtd dtd = new Dtd();
		ElementRule rule = new ElementRule();
		dtd.addElementRule("root", rule);
		String in = "<root/> <root> <a/> </root>";
		XmlReader reader = new XmlReader(new StringReader(in), dtd);
		XmlSParser parser = new XmlSParser(reader);
		// do stuff
		Element e = parser.startTag();
		assertEquals("root", e.getName());
		Element end = parser.endTag();
		assertEquals("root null", null, end);
		assertTrue("root", !e.isOpen());
		assertTrue("more data true", reader.hasMoreData());
		// try another doc
		Element st = parser.startTag();
		assertEquals("root", st.getName());
		parser.getContent(st);
		Element a = (Element)st.getChild(0);
		assertTrue("closed a", !a.isOpen());
		parser.up(0);
		assertTrue("more data false", !reader.hasMoreData());
	}

	// tests up() method
	public void testParserUp() 
		throws Exception
	{
		XmlSParser parser = newParser();
		parser.next();
		assertEquals("test", parser.startTag().getName());
		assertEquals("a", parser.startTag().getName());
		int depth = parser.getDepth();
		assertEquals("b", parser.startTag().getName());
		assertEquals("c", parser.startTag().getName());
		parser.up(depth);
		assertEquals("b2", parser.startTag().getName());
		assertEquals("b2", parser.endTag().getName());
		assertEquals("a", parser.endTag().getName());
		assertEquals("test", parser.endTag().getName());
		assertEquals(null, parser.endTag());
	}

	/** 
	 * Tests whole tree parsing.
	 */
	public void testWholeTreeParsing() 
		throws Exception
	{
		XmlSParser parser = newParser();
		parser.next();
		assertEquals(parser.startTag().getName(), "test");
		assertEquals(parser.startTag().getName(), "a");
		assertEquals(parser.startTag().getName(), "b");
		assertEquals(parser.startTag().getName(), "c");
		assertEquals(parser.endTag().getName(), "c");
		Element e = parser.startTag();
		assertEquals(e.getName(), "d");
		parser.getContent(e);
		assertEquals(e.getName(), "d");
		assertEquals(parser.endTag().getName(), "d");
		assertEquals(parser.endTag().getName(), "b");
		assertEquals(parser.startTag().getName(), "b2");
		parser.close();
	}

	public void testGetAttNameEOF()
		throws Exception
	{
		Reader r = new StringReader("<foo vers");
		XmlSParser p = new XmlSParser(new XmlReader(r));
		assertEquals("STAG", XmlEvent.STAG, p.next());
		String name = p.getAttName();
		assertEquals("Figured out name", "vers", name);
	}

	public void testCopyContentEOF()
		throws Exception
	{
		Reader r = new StringReader("<a> blah");
		XmlReader xr = new XmlReader(r);
		XmlSParser p = new XmlSParser(xr);
		p.next();
		try {
			p.copyContent(NullWriter.getInstance());
			fail("EOF in copyContent");
		} catch (XmlException e) {
		}
	}

	public void testCopyKnownEntity()
		throws Exception
	{
		Reader r = new StringReader("<a>the &known; here</a>");
		XmlReader xr = new XmlReader(r);
		xr.getDtd().addEntity("known", new Entity("value"));
		XmlSParser p = new XmlSParser(xr);
		p.next();
		p.next();
		String s = p.getText();
		assertEquals("the value here", s);
	}

	public void testGetAttNameBad()
		throws Exception
	{
		Reader r = new StringReader("<foo|vers");
		XmlSParser p = new XmlSParser(new XmlReader(r));
		p.next();
		try {
			p.getAttName();
			fail("space after name");
		} catch (XmlException e) {
		}
	}


	public void testGetAttValueEOF()
		throws Exception
	{
		Reader r = new StringReader("<foo vers='ab");
		XmlSParser p = new XmlSParser(new XmlReader(r));
		assertEquals("STAG", XmlEvent.STAG, p.next());
		try {
			p.getAttValue();
			fail("EOF in value");
		} catch (XmlException e) {
		}
		p.setReadString("<foo ve");
		try {
			p.getAttValue();
			fail("Must be on STAG");
		} catch (IllegalStateException e) {
		}
		p.next();
		String a = p.getAttValue("x");
		assertEquals("Use default", "x", a);
	}

	public void testGetAttValue()
		throws Exception
	{
		Reader r = new StringReader("<foo version='1.0'  ref=\"/bar/\" >text</foo>");
		XmlSParser p = new XmlSParser(new XmlReader(r));
		assertEquals("STAG", XmlEvent.STAG, p.next());
		String foo = p.getTagName();
		assertEquals("foo", foo);
		String versionn = p.getAttName();
		assertEquals("version", versionn);
		String versionv = p.getAttValue(null);
		assertEquals("version", "1.0", versionv);
		String ref     = p.getAttValue(null);
		assertEquals("ref", "/bar/", ref);
		String dflt    = "whatever";
		String notHere;
		notHere = p.getAttValue(dflt);
		assertEquals("dflt", notHere, dflt);
		notHere = p.getAttValue(dflt);
		assertEquals("dflt", notHere, dflt);
		// hmm
		assertEquals("PI" + p, XmlEvent.CHARDATA, p.next());
		assertEquals("text", p.getText());
	}

	public void testGetText()
		throws Exception
	{
		// we want to make sure we can get really long strings 
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < 256; i++)
			sb.append("stuff");
		String doc = "<foo>" + sb + "</foo>";
		XmlSParser p = new XmlSParser(new XmlReader());
		p.setReadString(doc);
		p.next(); // stag
		p.next(); // chardata
		assertEquals("text", sb.toString(), p.getText());
		p.setReadString(doc);
		p.next();
		p.next();
		assertEquals("text", sb.toString(), p.getText());
	}

	public void testSmartGetText()
		throws Exception
	{
		String doc = "<foo>text<bar>text2</bar>text3</foo>";
		XmlSParser p = new XmlSParser();
		p.setReadString(doc);
		p.setEvents(XmlEvent.STAG);
		p.setReadString(doc);
		p.next();
		assertEquals("text", p.getText());
		p.next(XmlEvent.STAG);
		assertEquals("text2", p.getText());
		p.next(XmlEvent.ETAG);
		assertEquals("text3", p.getText());
	}

	public void testNext2()
		throws Exception
	{
		String doc = "<foo><bar></bar>text3</foo>";
		XmlSParser p = new XmlSParser();
		p.setReadString(doc);
		p.setEvents(XmlEvent.COMMENT);
		assertEquals("STAG", XmlEvent.ETAG, p.next(XmlEvent.ETAG));
		assertEquals("TEXT", XmlEvent.CHARDATA, p.next(XmlEvent.TEXT));
		assertEquals("STAG", XmlEvent.EOD, p.next(XmlEvent.EOD));
	}

	public void testGetAtty()
		throws Exception
	{
		Reader r = new StringReader("<foo version='1.0'  ref=\"/bar/\" / >");
		XmlSParser p = new XmlSParser(new XmlReader(r));
		try {
			p.getAttName();
			fail("Must be on STAG");
		} catch (IllegalStateException e) {
		}
		p.next();
		String versionn = p.getAttName();
		assertEquals("version", versionn);
		p.getAttValue(null);
		String refn = p.getAttName();
		assertEquals("ref", refn);
		p.next();
	}

	public void testGetAtty2()
		throws Exception
	{
		Reader r = new StringReader("<foo >");
		XmlSParser p = new XmlSParser(new XmlReader(r));
		p.next();
		String crap = p.getAttName();
		assertEquals("crap", null, crap);
		String crap2 = p.getAttName();
		assertEquals("crap2", null, crap2);
		String crap3 = p.getAttValue();
		assertEquals("crap3", null, crap3);
		String crap4 = p.getAttName();
		assertEquals("crap4", null, crap4);
	}

	public void testGetAttValue2()
		throws Exception
	{
		Reader r = new StringReader("<foo version='1/0' />");
		XmlSParser p = new XmlSParser(new XmlReader(r));
		assertEquals("STAG", XmlEvent.STAG, p.next());
		String version = p.getAttValue(null);
		assertEquals("version", "1/0", version);
		p.getAttValue(null);
		p.getAttValue(null);
		assertEquals("fake ETAG" + p, XmlEvent.ETAG, p.next());
	}

	public void testMatches()
		throws Exception
	{
		final char foo[] = "foo".toCharArray();
		final char bar[] = "bar".toCharArray();
		Reader r = new StringReader(
			"<foo version='1/0' />bar<?foo ?><!--bary-->");
		XmlSParser p = new XmlSParser(new XmlReader(r), XmlEvent.ALL_EVENTS);
		assertEquals("STAG", XmlEvent.STAG, p.next());
		assertTrue("foo", p.matches(foo));
		assertTrue("foobar", !p.matches(bar));
		assertEquals("ETAG", XmlEvent.ETAG, p.next());
		assertEquals("CHARDATA", XmlEvent.CHARDATA, p.next());
		assertTrue("bar", p.matches(bar));
		assertEquals("PI", XmlEvent.PI, p.next());
		assertTrue("foo pi", p.matches(foo));
		assertEquals("COMMENT", XmlEvent.COMMENT, p.next());
		assertTrue("bar", p.matches(bar));
	}

	public void testExample()
		throws Exception
	{
		String document = 
				"<?xml version='1.0'?>\n" +
				"<doc>\n" +
						"<empty1 attr='value1'/>\n" +
						"<empty2 attr='value2'/>\n" +
						"<para>Hello world!</para>\n" +
				"</doc>\n";
		java.io.Reader r = new java.io.StringReader(document);
		XmlSParser parser = new XmlSParser(new XmlReader(r));
		parser.next();        // returns XmlEvent.STAG for 'doc' tag
		assertEquals(0, parser.getDepth());
		parser.next();        // returns XmlEvent.STAG for empty1 tag
		assertEquals(1, parser.getDepth());
		Element e = parser.startTag();
		String v1 = 
		e.getAttValue("attr"); // returns 'value1' attribute value from empty1
		assertTrue("not null", v1 != null);
		parser.next();        // returns XmlEvent.ETAG for empty1 tag
		parser.next();        // returns XmlEvent.STAG for empty2 tag
		assertEquals(1, parser.getDepth());
		String v2 =
		parser.getAttValue(); // returns 'value2' attribute value from empty2
		assertTrue("not null", v2 != null);
		parser.next();        // returns XmlEvent.ETAG for empty2 tag
		parser.next();        // returns XmlEvent.STAG for para tag
		assertEquals(1, parser.getDepth());
		parser.next();        // returns XmlEvent.CHARDATA for text
		assertEquals(2, parser.getDepth());
		String text = parser.getText(); // returns 'Hello world!'
		assertEquals("hello", "Hello world!", text);
		parser.next();        // returns XmlEvent.ETAG for para tag
		assertEquals(2, parser.getDepth());
		parser.next();        // returns XmlEvent.ETAG for 'doc' tag
		assertEquals(1, parser.getDepth());
		parser.next();        // returns XmlEvent.EOD
		assertEquals(0, parser.getDepth());
	}

	public void testExample2()
		throws Exception
	{
		String document = 
				"<?xml version='1.0'?>\n" +
				"<doc>\n" +
						"<empty1/>\n" +
						"text\n"+
						"<empty1></empty1>\n" +
						"<a/>\n"+
						"<empty1/>\n" +
				"</doc>\n";
		java.io.Reader r = new java.io.StringReader(document);
		XmlSParser parser = new XmlSParser(new XmlReader(r));
		parser.next();        // returns XmlEvent.STAG for 'doc' tag
		assertEquals(0, parser.getDepth());
		parser.next();        // returns XmlEvent.STAG for empty1 tag
		parser.getElementTree();
		assertEquals(XmlEvent.CHARDATA, parser.next());
		assertEquals(XmlEvent.STAG, parser.next());
		parser.getElementTree();
		assertEquals(XmlEvent.STAG, parser.next());
		assertEquals(XmlEvent.ETAG, parser.next());
		assertEquals(XmlEvent.STAG, parser.next());
		parser.getElementTree();
		parser.next();        // returns XmlEvent.ETAG for doc tag
		assertEquals(1, parser.getDepth());
		parser.next();        // returns XmlEvent.EOD
		assertEquals(0, parser.getDepth());
	}

	public void testExample3()
		throws Exception
	{
		String document = 
				"<?xml version='1.0'?>\n" +
				"<doc>\n" +
						"<empty1></empty1>\n" +
						"<a></a>\n"+
				"</doc>\n";
		java.io.Reader r = new java.io.StringReader(document);
		XmlSParser parser = new XmlSParser(new XmlReader(r));
		assertEquals(XmlEvent.STAG, parser.next());
		assertEquals(XmlEvent.STAG, parser.next());
		assertEquals(XmlEvent.ETAG, parser.next());
		Element e;
		e = parser.endTag();
		assertEquals("empty1", e.getName());
		assertEquals(XmlEvent.STAG, parser.next());
		assertEquals(XmlEvent.ETAG, parser.next());
		e = parser.endTag();

		assertEquals(XmlEvent.ETAG, parser.next());
		assertEquals(XmlEvent.EOD, parser.next());
		assertEquals(0, parser.getDepth());
	}

	public void testRef()
		throws Exception
	{
		Reader r = new StringReader("<foo>&lt;&gt;</foo>");
		XmlSParser parser = new XmlSParser(new XmlReader(r));
		assertEquals(XmlEvent.STAG, parser.next());
		assertEquals(XmlEvent.REFERENCE, parser.next());
		String s = parser.getText();
		assertEquals("references in body", "<>", s);
	}


	public void testReadTagName()
		throws Exception
	{
		XmlReader xr = new XmlReader();
		XmlSParser parser = new XmlSParser(xr);
		// this is NOT valid XML
		parser.setReadString("<a:b></c>");
		NamespaceImpl ns = new NamespaceImpl();
		parser.next();
		parser.readTagName(ns);
		assertEquals(null, ns.getNamespaceURI());
		assertEquals("a", ns.getPrefix());
		assertEquals("b", ns.getLocalName());
		assertEquals("a:b", ns.getName());
		parser.next();
		parser.readTagName(ns);
		assertEquals(null, ns.getNamespaceURI());
		assertEquals(null, ns.getPrefix());
		assertEquals(null, ns.getLocalName());
		assertEquals("c", ns.getName());

		String URI = "http://example.net/xmlns";
		xr.getScanner().setUriMap(new UriMap());
		xr.getScanner().getUriMap().put("a", URI);
		parser.setReadString("<a:b/>");
		parser.next();
		parser.readTagName(ns);
		assertEquals(URI, ns.getNamespaceURI());
		assertEquals("a", ns.getPrefix());
		assertEquals("b", ns.getLocalName());
		assertEquals("a:b", ns.getName());
	}

	public void testReadAttNameExample()
		throws Exception
	{
		java.io.Reader r = new java.io.StringReader("<foo version='1.0' ref = 'bar'></foo>");
		XmlSParser p = new XmlSParser(new XmlReader(r));
		assertEquals(XmlEvent.STAG, p.next());
		assertEquals("version", p.getAttName());
		assertEquals("ref", p.getAttName());
		assertEquals(null, p.getAttName());
		assertEquals(null, p.getAttName());
	}

	public void testReadAttName()
		throws Exception
	{
		XmlSParser parser = new XmlSParser(new XmlReader());
		parser.setReadString("<a:b c:d = 'ef' g=\"h\" />");
		NamespaceImpl ns = new NamespaceImpl();
		parser.next();
		parser.readAttName(ns);
		assertEquals(null, ns.getNamespaceURI());
		assertEquals("c", ns.getPrefix());
		assertEquals("d", ns.getLocalName());
		assertEquals("c:d", ns.getName());

		parser.readAttName(ns);
		assertEquals(null, ns.getNamespaceURI());
		assertEquals(null, ns.getPrefix());
		assertEquals(null, ns.getLocalName());
		assertEquals("" + parser, "g", ns.getName());

		parser.readAttName(ns);
		assertTrue("clear ns", ns.isClear());
	}


	public void testReadPlain()
		throws Exception
	{
		XmlSParser parser = new XmlSParser(new XmlReader());
		// EOD will happen
		parser.setEvents(XmlEvent.CHARDATA | XmlEvent.STAG | XmlEvent.ETAG);
		parser.setReadString("apple <b>banana</b> cherry");
		assertEquals(XmlEvent.CHARDATA, parser.next());
		assertEquals(XmlEvent.STAG, parser.next());
		assertEquals(XmlEvent.CHARDATA, parser.next());
		assertEquals(XmlEvent.ETAG, parser.next());
		assertEquals(XmlEvent.CHARDATA, parser.next());
	}

	public void testCopy0()
		throws Exception
	{
		XmlSParser parser = new XmlSParser(new XmlReader());
		String s = " a document here ";
		parser.setReadString("<a><b>" + s + "</b><b>" + s + "</b></a>");
		XmlCharArrayWriter caw = new XmlCharArrayWriter();
		assertEquals(XmlEvent.STAG, parser.next());
		assertEquals(XmlEvent.STAG, parser.next());
		parser.copyContent(caw);
		assertEquals(s, caw.toString());
		assertEquals(XmlEvent.ETAG, parser.next());
		assertEquals(XmlEvent.STAG, parser.next());
		caw.reset();
		parser.copyContent(caw);
		assertEquals(s, caw.toString());
		assertEquals(XmlEvent.ETAG, parser.next());
		assertEquals(XmlEvent.ETAG, parser.next());
	}

	public void testCopy()
		throws Exception
	{
		XmlSParser parser = new XmlSParser(new XmlReader());
		String s = " a document <a>was <b>definately</b> </a> here ";
		parser.setReadString("<b>" + s + "</b>");
		XmlCharArrayWriter caw = new XmlCharArrayWriter();
		try {
			parser.copyContent(caw);
			fail("not on STAG");
		} catch (IllegalStateException e) { }
		assertEquals(XmlEvent.STAG, parser.next());
		parser.copyContent(caw);
		assertEquals(XmlEvent.ETAG, parser.next());
	}

	public void testCopy2()
		throws Exception
	{
		XmlSParser parser = new XmlSParser(new XmlReader());
		String s = "I <a>was <b/><!--com</ />ment-->definately<b at='bat' /> </a> <![CDATA[h<er>e]]>";
		// String s = "I <a>was <b/><!--com</ />ment-->definately<b at='bat' /> </a> here ";
		parser.setReadString("<b>" + s + "</b><c/>");

		XmlCharArrayWriter caw = new XmlCharArrayWriter();
		assertEquals(XmlEvent.STAG, parser.next());
		parser.copyContent(caw);
		assertEquals(s, caw.toString());
		assertEquals(XmlEvent.ETAG, parser.next());
		assertEquals("" + parser, XmlEvent.EOD, parser.next());
		assertEquals("" + parser, XmlEvent.STAG, parser.next());
		assertEquals("" + parser, XmlEvent.ETAG, parser.next());
	}

	public void testCopy3()
		throws Exception
	{
		XmlSParser parser = new XmlSParser(new XmlReader());
		String s = "I <a>was <b/><!--com</ />ment-->definately<b at='b/a/t' /> </a> <!DOCTYPE[]]>";
		parser.setReadString("<b>" + s + "</b>");
		assertEquals(XmlEvent.STAG, parser.next());
		XmlCharArrayWriter caw = new XmlCharArrayWriter();
		parser.copyContent(caw);
		assertEquals(s, caw.toString());
	}

	public void testCopyNull()
		throws Exception
	{
		XmlSParser parser = new XmlSParser(new XmlReader());
		parser.setReadString("<null something='here' />   <another/>");
		assertEquals(XmlEvent.STAG, parser.next());
		// should be able to get stuff
		parser.getTagName();
		parser.getAttValue();
		parser.getAttName();

		XmlCharArrayWriter caw = new XmlCharArrayWriter();
		parser.copyContent(caw);
		assertEquals("", caw.toString());

		assertEquals("empty tag returns 'fake' ETAG", 
				XmlEvent.ETAG, parser.next());
		// white space was eaten
		assertEquals(XmlEvent.STAG, parser.next());
	}

	public void testName()
		throws Exception
	{
		XmlSParser parser = new XmlSParser(new XmlReader());
		parser.setReadString("<null^something='here' />");
		try {
			assertEquals("something", parser.getAttName());
			fail("need a space!");
		} catch (IllegalStateException e) {
		}
	}

	public void testEmptyDoc()
		throws Exception
	{
		XmlSParser parser = new XmlSParser();
		assertEquals(XmlEvent.EOD, parser.next());
	}

	public void testExtractComments()
		throws Exception
	{
		XmlSParser parser = new XmlSParser();
		parser.setReadString(" <!-- x --> <a>  <!-- y --> <b> </b> <!-- z --> </a>");
		parser.setEvents(XmlEvent.COMMENT | XmlEvent.EOD);
		while (parser.next() != XmlEvent.EOD) {
			parser.getComment();
			// System.out.println("comment " + c);
		}
	}

	public void testBadComment()
		throws Exception
	{
		XmlSParser parser = new XmlSParser();
		parser.setReadString("<!-- -- -->");
		try {
			parser.next();
			fail("-- in comment");
		} catch (XmlException e) {
		}
	}

	public void testForCodeCoverage2()
		throws Exception
	{
		XmlSParser parser = new XmlSParser();
		parser.setReader(new StringReader("<doc><a/> </doc>"));
		parser.setSkipWS(false);
		parser.next();
		assertEquals(XmlEvent.STAG, parser.next());
		StringWriter sw = new StringWriter();
		// don't touch space
		parser.copyContent(sw);
		assertEquals("", sw.toString());
		assertEquals(XmlEvent.ETAG, parser.next());
		assertEquals(XmlEvent.CHARDATA, parser.next());
		assertEquals(XmlEvent.ETAG, parser.next());

		// don't touch space
		parser.setReader(new StringReader("<doc><a/> </doc>"));
		parser.setSkipWS(false);
		parser.next();
		assertEquals(XmlEvent.STAG, parser.next());
		Element elem = parser.startTag();
		assertEquals("a", elem.getName());
		assertEquals(XmlEvent.ETAG, parser.next());
		assertEquals(XmlEvent.CHARDATA, parser.next());

		parser.setReader(new StringReader("<!XX>"));
		try {
			parser.next();
			fail("Unknown token");
		} catch (XmlException e) {
		}

		parser.setReader(new StringReader("<?PI x?>"));
		parser.setEvents(XmlEvent.PI);
		parser.next();
		PI pi = parser.getPI();
		assertEquals("PI", pi.getName());
		assertEquals("x", pi.getData());
	}

	public void testSkip()
		throws Exception
	{
		XmlSParser parser = new XmlSParser();
		parser.setReadString("<a> a &ent; <![CDATA[ xyz ]]> b </a>");
		parser.next();
		parser.next();
		assertEquals(XmlEvent.ETAG, parser.next());
	}

	public void testForCodeCoverage()
		throws Exception
	{
		XmlSParser parser = new XmlSParser();
		XmlSParser.eventToString(XmlEvent.ALL_EVENTS);
		XmlSParser.eventToString(XmlEvent.NONE);
		XmlSParser.eventToString(XmlEvent.TEXT);
		XmlSParser.eventToString(333333);
		XmlSParser.eventToString(XmlEvent.EOD);
		parser.setReader(new StringReader("<doc/>"));
		parser.next();

		parser.setReader(new StringReader("<a><![CDATA[ Hell>]>] ]]></a>"));
		parser.next();
		assertEquals(XmlEvent.CDSECT, parser.next());
		assertEquals(XmlEvent.ETAG, parser.next());

		parser.setReader(new StringReader("<a><!-- a->--></a>"));
		parser.setEvents(XmlEvent.STAG | XmlEvent.ETAG | XmlEvent.TEXT | XmlEvent.COMMENT);
		assertEquals(XmlEvent.STAG, parser.next());
		parser.next();
		assertTrue(parser.isEvent(XmlEvent.COMMENT));
		assertNull(parser.startTag());
		parser.next();
		assertTrue(parser.isEvent(XmlEvent.ETAG));

		parser.setReader(new StringReader("<a> &amp;<![CDATA[k]]></a>"));
		parser.setSkipWS(false);
		parser.setSkipRepeatTextEvents(false);
		parser.next();
		assertTrue(parser.startTag() != null);
		assertEquals(XmlEvent.CHARDATA, parser.next());
		try {
			parser.getTagName();
			fail("cannot copy from reference");
		} catch (IllegalXmlParserStateException e) {
		}
		assertEquals(XmlEvent.REFERENCE, parser.next());
		assertEquals(XmlEvent.CDSECT, parser.next());
		StringWriter sw = new StringWriter();
		parser.copyText(sw);
		assertEquals("k", sw.toString());
		// assertEquals(XmlEvent.CHARDATA, parser.next());
		parser.next();
		assertEquals("a", parser.getTagName());
	}

	public void testCanonicalText()
		throws Exception
	{
		XmlSParser xs = new XmlSParser(new XmlReader(), XmlEvent.CHARDATA);
		xs.setReadString("<a><b>Fo&apos;o</b><b>Fo'o</b></a>");
		xs.next();
		String foo1 = xs.getCanonicalText();
		xs.next();
		String foo2 = xs.getCanonicalText();
		assertTrue(foo1.equals("Fo'o"));
		assertTrue(foo1 == foo2);
	}

	public void testStringCtr()
		throws Exception
	{
		XmlSParser xs = new XmlSParser();
		xs.setReadString("<a>test</a>");
		xs.next();
		assertEquals("test", xs.getText());
	}

	public void testCanonicalText2()
		throws Exception
	{
		XmlReader xr = new XmlReader();
		StringPool sp = xr.getStringPool();
		XmlSParser xs = new XmlSParser(xr, XmlEvent.CHARDATA);
		xs.setReadString("<a><b>Fo&apos;o</b><b>Fo'o</b></a>");
		xs.next();
		String foo1 = sp.intern(xs.getText());
		xs.next();
		String foo2 = sp.intern(xs.getText());
		assertTrue(foo1.equals("Fo'o"));
		assertTrue(foo1 == foo2);
	}

	/*
	public void testTooManyETAG()
		throws Exception
	{
		Reader r = new StringReader("<bar></bar></foo>");
		XmlSParser p = new XmlSParser(new XmlReader(r), 
			XmlEvent.STAG | XmlEvent.ETAG);
		assertEquals("" + p, XmlEvent.STAG, p.next());
		assertEquals("" + p, XmlEvent.ETAG, p.next());
		try {
			p.next(); // EOD
			p.next(); // ETAG
			p.next(); // FAIL!
			fail("Should get too many end tags");
		} catch (XmlException e) {
		}
	}
	*/
}
