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

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

	public void testNoContent() 
		throws Exception
	{
		XmlReader reader;
		XmlParser parser;
		reader = new XmlReader(new StringReader("<root>"));
		parser = new XmlParser(reader);
		
		// no content here
		try {
			parser.getContent();
			fail("Should get no content " + parser);
		} catch (XmlException e) {
		}

		// no endTag here
		try {
			parser.startTag();
			parser.getContent();
			fail("Should get EOF Exception " + parser);
		} catch (XmlException e) {
		}
	}

	// tests various rules
	public void testElementRules() 
		throws Exception
	{
		XmlWriter writer;
		StringWriter sw = new StringWriter();
		writer = new XmlWriter(new BufferedWriter(sw));
		Element root = new Element("test");
		Element a = new Element("a");
		writer.startElement(root);
			writer.startElement(a);
				writer.writeCData(D_CONTENT);
			writer.endElement();
		writer.endElement();
		writer.flush();
		String in = sw.getBuffer().toString();
		XmlReader reader;
		XmlParser parser;
		ElementRule rule;

		// test unknown element rule
		reader = new XmlReader(new StringReader(in));
		rule = new ElementRule();
		reader.getDtd().addElementRule(root, rule);
		parser = new XmlParser(reader);
		try {
			parser.startTag();
			parser.startTag();
			fail("Should raise XmlException " + parser);
		} catch (XmlException xre) { }

		// test no pcdata rule
		reader = new XmlReader(new StringReader(in));
		parser = new XmlParser(reader);
		rule = new ElementRule();
		rule.setAllowPCData(false);
		reader.getDtd().addElementRule(root, rule);
		reader.getDtd().addElementRule(a, rule);
		try {
			parser.startTag();
			parser.startTag();
			parser.getContent();
			fail("Should raise ElementRuleException " + parser);
		} catch (XmlException xre) { }

		// test not allowed rule
		reader = new XmlReader(new StringReader(in));
		parser = new XmlParser(reader);
		rule = new ElementRule();
		rule.allowNoElements();
		rule.allowElement("b");
		reader.getDtd().addElementRule(root, rule);
		reader.getDtd().addElementRule(a, rule);
		try {
			parser.startTag();
			parser.startTag();
			parser.endTag();
			fail("Should raise ElementRuleException");
		} catch (ElementRuleException xre) { }
	}

	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 XmlParser 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();
		//System.out.println(in);

		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 XmlParser(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);
		XmlParser parser = new XmlParser(reader);
		// do stuff
		parser.skipProlog();
		assertEquals("root", parser.startTag().getName());
		Element e = parser.endTag();
		assertEquals("root", e.getName());
		assertTrue("root", !e.isOpen());
		assertTrue("more data true", reader.hasMoreData());
		// try another doc
		parser.skipProlog();
		assertEquals("root", parser.startTag().getName());
		Element a = (Element)parser.getContent().getChild(0);
		assertTrue("closed a", !a.isOpen());
		parser.up(0);
		assertTrue("more data false", !reader.hasMoreData());
	}

	// tests up() method
	public void testParserUp() 
		throws Exception
	{
		XmlParser parser = newParser();
		parser.skipProlog();
		assertEquals(null, parser.getTopElement());
		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());
		try {
			parser.endTag();
		} catch (XmlException xre) {
			return;
		}
		fail("Should raise XmlException");
	}

	/** 
	 * Tests skip misc.
	 */
	public void testSkipMisc() 
		throws Exception
	{
		XmlParser parser = newParser();
		Reader r = new StringReader("<test><a><!-- --></a><?pi ?></test> <!-- --> <?pi ?>");
		parser.setReader(r);
		parser.alwaysSaveMisc(true);
		parser.toString();
		assertEquals(parser.startTag().getName(), "test");
		parser.toString();
		assertEquals(parser.startTag().getName(), "a");
		parser.emptyContent();
		assertEquals(null, parser.startTag());
		assertEquals(parser.endTag().getName(), "a");
		parser.emptyContent();

		assertEquals(parser.endTag().getName(), "test");
		parser.skipMisc();
		assertEquals(-1, r.read());
	}

	/** 
	 * Tests whole tree parsing.
	 */
	public void testWholeTreeParsing() 
		throws Exception
	{
		XmlParser parser = newParser();
		parser.skipProlog();
		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");
		assertEquals(parser.startTag().getName(), "d");
		assertEquals(parser.getContent().getName(), "d");
		assertEquals(D_CONTENT, parser.getTopElement().getCharacterData().trim());
		assertEquals(parser.endTag().getName(), "d");
		assertEquals(parser.endTag().getName(), "b");
		assertEquals(parser.startTag().getName(), "b2");
		parser.close();
	}
}
