/*
 * Copyright (c) 2008, Aaron Digulla
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *     * Neither the name of Aaron Digulla nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package de.pdark.decentxml;

import static org.junit.Assert.*;

import de.pdark.decentxml.XMLTokenizer.Type;
import java.io.ByteArrayInputStream;
import java.util.List;
import java.util.Map;
import org.junit.After;
import org.junit.Test;

public class XMLParserTest {
  public static final String POM_XML =
      "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
          + "\r\n"
          + "<project xmlns=\"http://maven.apache.org/POM/4.0.0\""
          + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
          + " xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0"
          + " http://maven.apache.org/maven-v4_0_0.xsd\">\r\n"
          + "  <modelVersion>4.0.0</modelVersion>\r\n"
          + "\r\n"
          + "  <!-- Ignore this <parent>\r\n"
          + "    <groupId>org.codehaus.mojo</groupId>\r\n"
          + "    <artifactId>mojo</artifactId>\r\n"
          + "    <version>15</version>\r\n"
          + "  </parent> -->\r\n"
          + "  <parent>\r\n"
          + "    <groupId>org.codehaus.mojo</groupId>\r\n"
          + "    <artifactId>mojo</artifactId>\r\n"
          + "    <version>16</version>\r\n"
          + "  </parent>\r\n"
          + "\r\n"
          + "  <groupId>org.codehaus.mojo</groupId>\r\n"
          + "  <artifactId>versions-maven-plugin</artifactId>\r\n"
          + "  <version>1.0-SNAPSHOT</version>\r\n"
          + "  <packaging>maven-plugin</packaging>\r\n"
          + "</project>\r\n";

  @Test
  public void testRoundtrip() throws Exception {
    setUp(XMLTokenizerTest.XML);

    assertEquals(XMLTokenizerTest.XML, doc.toXML());
  }

  @Test
  public void testCopy() throws Exception {
    setUp(XMLTokenizerTest.XML);

    assertEquals(doc.toXML(), doc.copy().toXML());
  }

  @Test
  public void testBOM() throws Exception {
    XMLParser parser = new XMLParser();
    byte[] data = XMLTokenizerTest.XML.getBytes("UnicodeBig");
    assertEquals(XMLTokenizerTest.XML.length() * 2 + 2, data.length);
    assertEquals(-2, data[0]);
    assertEquals(-1, data[1]);
    ByteArrayInputStream in = new ByteArrayInputStream(data);
    XMLIOSource source = new XMLIOSource(in);
    assertEquals(XMLTokenizerTest.XML.length(), source.length());
    assertEquals('<', source.charAt(0));
    doc = parser.parse(source);
  }

  @Test
  public void testNavigation() throws Exception {
    // System.out.println (XML);
    setUp(XMLTokenizerTest.XML);

    Element root = doc.getRootElement();
    assertNotNull(root);

    Element e = root.getChild("e");
    assertNotNull(e);
    assertEquals("e", e.getName());
  }

  @Test
  public void testNavigation2() throws Exception {
    setUp(XMLTokenizerTest.XML);

    Element root = doc.getRootElement();
    List<Element> l = root.getChildren("e");
    assertNotNull(l);
    assertEquals(1, l.size());
    assertEquals(root.getChild("e"), l.get(0));
  }

  @Test
  public void testNavigation3() throws Exception {
    setUp(XMLTokenizerTest.XML);

    Element root = doc.getRootElement();
    List<Element> l = root.getChildren("a");
    assertNotNull(l);
    assertEquals(2, l.size());
  }

  @Test
  public void testNavigation4() throws Exception {
    setUp(XMLTokenizerTest.XML);

    Element root = doc.getRootElement();
    List<Element> l = root.getChildren("a");
    Element a = l.get(0);
    Attribute attr = a.getAttribute("x");
    assertEquals("x", attr.getName());
    assertEquals("1", attr.getValue());
  }

  @Test
  public void testDocumentType() throws Exception {
    setUp(XMLTokenizerTest.XML);

    assertEquals(Type.DOCUMENT, doc.getType());
  }

  @Test
  public void testElementType() throws Exception {
    setUp(XMLTokenizerTest.XML);

    assertEquals(Type.ELEMENT, doc.getRootElement().getType());
  }

  @Test
  public void testElementChild() throws Exception {
    setUp("<a/>");

    assertNull(doc.getRootElement().getChild("xxx"));
  }

  @Test
  public void testCreateElement() throws Exception {
    Element e = new Element("e");
    assertEquals(-1, e.getStartOffset());
    assertEquals(-1, e.getEndOffset());
  }

  @Test
  public void testRemove() throws Exception {
    setUp(XMLTokenizerTest.XML);

    Node n;
    n = doc.removeNode(2);
    assertEquals(Type.COMMENT, n.getType());
    n = doc.getNode(2);
    assertTrue(doc.removeNode(n));

    Element root = doc.getRootElement();
    root.clearNodes();

    root.addNode(new Text("a")).addNode(new Text("c")).addNode(1, new Text("b"));

    assertEquals(
        "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "\n" + "<root>abc</root>\n" + "",
        doc.toXML());
  }

  @Test
  public void testAttributeMap() throws Exception {
    setUp("<a/>");

    Map<String, Attribute> map = doc.getRootElement().getAttributeMap();
    assertEquals("{}", map.toString());
  }

  @Test
  public void testAttributeMap2() throws Exception {
    setUp("<a />");

    Map<String, Attribute> map = doc.getRootElement().getAttributeMap();
    assertEquals("{}", map.toString());
  }

  @Test
  public void testAttributeMap3() throws Exception {
    setUp("<a x='1' y='2' />");

    Map<String, Attribute> map = doc.getRootElement().getAttributeMap();
    assertEquals("{x= x='1', y= y='2'}", map.toString());
  }

  @Test
  public void testAttributeMapSequence() throws Exception {
    setUp("<a y='2' x='1' />");

    Map<String, Attribute> map = doc.getRootElement().getAttributeMap();
    assertEquals("{y= y='2', x= x='1'}", map.toString());
  }

  @Test
  public void testAttributes() throws Exception {
    setUp("<a/>");

    List<Attribute> list = doc.getRootElement().getAttributes();
    assertEquals("[]", list.toString());
  }

  @Test
  public void testAttributes2() throws Exception {
    setUp("<a />");

    List<Attribute> list = doc.getRootElement().getAttributes();
    assertEquals("[]", list.toString());
  }

  @Test
  public void testAttributes3() throws Exception {
    setUp("<a y='2' x='1' />");

    List<Attribute> list = doc.getRootElement().getAttributes();
    assertEquals("[ y='2',  x='1']", list.toString());
  }

  @Test
  public void testAttributes4() throws Exception {
    Attribute a = new Attribute("a", "\"", '"');
    assertEquals(" a=\"&quot;\"", a.toXML());
  }

  @Test
  public void testAttributes5() throws Exception {
    Attribute a = new Attribute("a", "'", '\'');
    assertEquals(" a='&apos;'", a.toXML());
  }

  @Test
  public void testAttributes6() throws Exception {
    Attribute a = new Attribute("a", "\"'", '\'');
    assertEquals(" a='\"&apos;'", a.toXML());
  }

  @Test
  public void testAttributes7() throws Exception {
    Attribute a = new Attribute("a", "\"'", '\'');
    a.setQuoteChar('"');
    assertEquals(" a=\"&quot;'\"", a.toXML());
  }

  @Test
  public void testAttributes8() throws Exception {
    Attribute a = new Attribute("a", "x");
    assertEquals(" a=\"x\"", a.toXML());
    assertEquals('\"', a.getQuoteChar());
    a.setQuoteChar('\'');
    assertEquals('\'', a.getQuoteChar());
    assertEquals(" a='x'", a.toXML());
  }

  @Test
  public void testAttributesNameNull() throws Exception {
    try {
      new Attribute(null, null);
      fail("No exception was thrown");
    } catch (IllegalArgumentException ex) {
      assertEquals("name is null", ex.getMessage());
    }
  }

  @Test
  public void testAttributesValueNull() throws Exception {
    try {
      new Attribute("a", null);
      fail("No exception was thrown");
    } catch (IllegalArgumentException ex) {
      assertEquals("value is null", ex.getMessage());
    }
  }

  @Test
  public void testIllegalQuoteChar() throws Exception {
    try {
      new Attribute("a", "\"", 'x');
      fail("No exception was thrown");
    } catch (XMLParseException ex) {
      assertEquals("Illegal quote charater: \"x\" (120)", ex.getMessage());
    }
  }

  @Test
  public void testGetAttribute() throws Exception {
    setUp("<a/>");

    Attribute a = doc.getRootElement().getAttribute("x");
    assertNull(a);
  }

  @Test
  public void testGetAttribute2() throws Exception {
    setUp("<a />");

    Attribute a = doc.getRootElement().getAttribute("x");
    assertNull(a);
  }

  @Test
  public void testGetAttribute3() throws Exception {
    setUp("<a y='2' x='1' />");

    Attribute a = doc.getRootElement().getAttribute("x");
    assertEquals(" x='1'", a.toString());
  }

  @Test
  public void testGetAttributeValue() throws Exception {
    setUp("<a/>");

    String a = doc.getRootElement().getAttributeValue("x");
    assertNull(a);
  }

  @Test
  public void testGetAttributeValue2() throws Exception {
    setUp("<a />");

    String a = doc.getRootElement().getAttributeValue("x");
    assertNull(a);
  }

  @Test
  public void testGetAttributeValue3() throws Exception {
    setUp("<a y='2' x='1' />");

    String a = doc.getRootElement().getAttributeValue("x");
    assertEquals("1", a.toString());
  }

  @Test
  public void testGetAttributeValue4() throws Exception {
    setUp("<a y='2' x='&lt;&gt;&amp;&quot;&apos;' />");

    String a = doc.getRootElement().getAttributeValue("x");
    assertEquals("<>&\"'", a.toString());
  }

  @Test
  public void testIsCompactEmpty() throws Exception {
    setUp("<a/>");
    assertTrue(doc.getRootElement().isCompactEmpty());
  }

  @Test
  public void testIsCompactEmpty2() throws Exception {
    setUp("<a />");
    assertTrue(doc.getRootElement().isCompactEmpty());
  }

  @Test
  public void testIsCompactEmpty3() throws Exception {
    setUp("<a></a>");
    assertFalse(doc.getRootElement().isCompactEmpty());
    assertEquals("<a></a>", doc.toXML());
  }

  @Test
  public void testIsCompactEmpty4() throws Exception {
    setUp("<a />");
    doc.getRootElement().addNode(new Element("e"));
    assertFalse(doc.getRootElement().isCompactEmpty());
  }

  @Test
  public void testGetChildNodes() throws Exception {
    setUp("<a />");
    assertEquals("[]", doc.getRootElement().getNodes().toString());
  }

  @Test
  public void testGetChildNodes2() throws Exception {
    setUp("<a> </a>");
    assertEquals("[ ]", doc.getRootElement().getNodes().toString());
  }

  @Test
  public void testRemoveChildNode() throws Exception {
    setUp("<a> <b/></a>");
    Node n = doc.getRootElement().removeNode(0);
    assertNotNull(n);
    assertEquals("<a><b/></a>", doc.toXML());
  }

  @Test
  public void testRemoveChildNode2() throws Exception {
    setUp("<a> <b/></a>");
    Node n = doc.getRootElement().removeNode(1);
    assertNotNull(n);
    assertEquals("<a> </a>", doc.toXML());
  }

  @Test
  public void testRemoveChildNode3() throws Exception {
    setUp("<a> <b/></a>");
    Node n = doc.getRootElement().getNode(0);
    assertTrue(doc.getRootElement().removeNode(n));
    assertEquals("<a><b/></a>", doc.toXML());
  }

  @Test
  public void testRemoveChildNode4() throws Exception {
    Element e = new Element("e");
    assertFalse(e.removeNode(null));
  }

  @Test
  public void testGetChildren() throws Exception {
    setUp("<a />");
    assertEquals("[]", doc.getRootElement().getChildren().toString());
  }

  @Test
  public void testGetChildren2() throws Exception {
    setUp("<a> <b/></a>");
    assertEquals("[<b/>]", doc.getRootElement().getChildren().toString());
  }

  @Test
  public void testGetText() throws Exception {
    setUp("<a />");
    assertEquals("", doc.getRootElement().getText());
  }

  @Test
  public void testGetText2() throws Exception {
    setUp("<a>  x \n y  </a>");
    assertEquals("  x \n y  ", doc.getRootElement().getText());
  }

  @Test
  public void testGetText3() throws Exception {
    setUp("<a>a<b>x</b>a</a>");
    assertEquals("axa", doc.getRootElement().getText());
  }

  @Test
  public void testGetTrimmedText() throws Exception {
    setUp("<a>  x \n y  </a>");
    assertEquals("x \n y", doc.getRootElement().getTrimmedText());
  }

  @Test
  public void testGetNormalizedText() throws Exception {
    setUp("<a> <b> x \n y </b> </a>");
    assertEquals("x y", doc.getRootElement().getNormalizedText());
  }

  @Test
  public void testNothingFound() throws Exception {
    setUp(POM_XML);

    Element match = root.getChild("xxx");
    assertNull(match == null ? "" : match.toString(), match);
  }

  @Test
  public void testProject() throws Exception {
    setUp(POM_XML);

    assertEquals("project", root.getName());
    assertEquals(42, root.getStartToken().getStartOffset());
  }

  @Test
  public void testParent() throws Exception {
    setUp(POM_XML);

    Element match = root.getChild("parent");
    assertEquals(437, match.getStartOffset());
    assertEquals(562, match.getEndOffset());

    assertEquals(
        "<parent>\r\n"
            + "    <groupId>org.codehaus.mojo</groupId>\r\n"
            + "    <artifactId>mojo</artifactId>\r\n"
            + "    <version>16</version>\r\n"
            + "  </parent>",
        match.getStartToken().getSource().substring(match.getStartOffset(), match.getEndOffset()));
  }

  @Test
  public void testParentVersion() throws Exception {
    setUp(POM_XML);

    Element match = root.getChild("parent");
    Element version = match.getChild("version");
    assertEquals(528, version.getStartToken().getStartOffset());
    assertEquals("<version>16</version>", version.toXML());
    assertEquals("16", version.getNormalizedText());
  }

  @Test
  public void testReplaceParentVersion() throws Exception {
    setUp(POM_XML);

    Element match = root.getChild("parent");
    Element version = match.getChild("version");
    version.setText("17");

    assertEquals(POM_XML.replaceAll("16", "17"), doc.toXML());
  }

  @Test
  public void testParentXXX() throws Exception {
    setUp(POM_XML);

    Element parent = root.getChild("parent");
    Element match = parent.getChild("xxx");
    assertNull(match == null ? "" : match.toString(), match);
  }

  @Test
  public void testProjectArtifactId() throws Exception {
    setUp(POM_XML);

    Element artifactId = root.getChild("artifactId");
    assertEquals("versions-maven-plugin", artifactId.getNormalizedText());
  }

  @Test
  public void testNoAttributeXXX() throws Exception {
    setUp("<a />");
    Attribute a = root.getAttribute("xxx");
    assertNull(a);
  }

  @Test
  public void testNoAttributeXXX2() throws Exception {
    setUp("<a x='1' />");
    Attribute a = root.getAttribute("xxx");
    assertNull(a);
  }

  @Test
  public void testSetAttributeName() throws Exception {
    setUp("<a x='1' />");
    Attribute a = root.getAttribute("x");
    a.setName("y");
    assertEquals("<a y='1' />", doc.toXML());
  }

  @Test
  public void testSetAttributeValue() throws Exception {
    setUp("<a x='1' />");
    Attribute a = root.getAttribute("x");
    a.setValue("2");
    assertEquals("<a x='2' />", doc.toXML());
  }

  @Test
  public void testSetAttributeValue2() throws Exception {
    setUp("<a x='1' />");
    Attribute a = root.getAttribute("x");
    a.setValue("\"x\"");
    assertEquals("<a x='\"x\"' />", doc.toXML());
  }

  @Test
  public void testSetAttributeValue3() throws Exception {
    setUp("<a x='1' />");
    Attribute a = root.getAttribute("x");
    a.setValue("'b'");
    assertEquals("<a x='&apos;b&apos;' />", doc.toXML());
  }

  @Test
  public void testSetAttributeValue4() throws Exception {
    setUp("<a x='1' />");
    Attribute a = root.getAttribute("x");
    a.setValue("'\"");
    assertEquals("<a x='&apos;\"' />", doc.toXML());
  }

  @Test
  public void testAttributeToXML() throws Exception {
    setUp("<a x='1' />");
    Attribute a = root.getAttribute("x");
    assertEquals(" x='1'", a.toXML());
  }

  @Test
  public void testAttributeType() throws Exception {
    setUp("<a x='1' />");
    Attribute a = root.getAttribute("x");
    assertEquals(Type.ATTRIBUTE, a.getType());
  }

  @Test
  public void testAttributeOffsets() throws Exception {
    setUp("<a x='1' />");
    Attribute a = root.getAttribute("x");
    assertEquals(2, a.getStartOffset());
    assertEquals(8, a.getEndOffset());
  }

  @Test
  public void testNoRootElement() throws Exception {
    try {
      setUp("<?xml version='1.0'?>");
      fail("No exception was thrown");
    } catch (XMLParseException e) {
      assertEquals("No root element found", e.getMessage());
    }
  }

  @Test
  public void testTwoRootElements() throws Exception {
    try {
      setUp("<?xml version='1.0'?><a /><b/>");
      fail("No exception was thrown");
    } catch (XMLParseException e) {
      assertEquals("Line 1, column 27: Only one root element allowed per document", e.getMessage());
    }
  }

  @Test
  public void testUnexpectedEOF() throws Exception {
    try {
      setUp("<?xml version='1.0'?>\n<a");
      fail("No exception was thrown");
    } catch (XMLParseException e) {
      assertEquals("Line 2, column 3: Missing '>' of start tag", e.getMessage());
    }
  }

  @Test
  public void testUnexpectedEOF2() throws Exception {
    try {
      setUp("<?xml version='1.0'?>\n<a>");
      fail("No exception was thrown");
    } catch (XMLParseException e) {
      assertEquals(
          "Line 2, column 1: Unexpected end-of-file while parsing children of element a",
          e.getMessage());
    }
  }

  @Test
  public void testElementMismatch() throws Exception {
    try {
      setUp("<?xml version='1.0'?>\n<a></b>");
      fail("No exception was thrown");
    } catch (XMLParseException e) {
      assertEquals(
          "Line 2, column 1: End element 'b' at line 2, column 4 doesn't match with 'a'",
          e.getMessage());
    }
  }

  @Test
  public void testElementMismatch2() throws Exception {
    try {
      setUp("<?xml version='1.0'?>\n<a>\n<b></b>\n<b>\n<b></b>\n</a>");
      fail("No exception was thrown");
    } catch (XMLParseException e) {
      assertEquals(
          "Line 4, column 1: End element 'a' at line 6, column 1 doesn't match with 'b'",
          e.getMessage());
    }
  }

  @Test
  public void testDocumentXMLDecl1() throws Exception {
    try {
      setUp("<?xml   version = '2.1' ?>\n<root />");
      fail("No exception was thrown");
    } catch (XMLParseException e) {
      assertEquals(
          "Line 1, column 19: only versions '1.0' and '1.1' are supported: [2.1]", e.getMessage());
    }
  }

  @Test
  public void testDocumentXMLDecl2() throws Exception {
    setUp("<?xml   version = '1.1'  encoding = 'Utf-8' ?>\n<root />");
    assertEquals("1.1", doc.getVersion());
    assertEquals("Utf-8", doc.getEncoding());
    assertFalse(doc.isStandalone());
  }

  @Test
  public void testDocumentXMLDecl3() throws Exception {
    setUp("<?xml   version = '1.1'  encoding = 'Utf-8'  standalone = 'yes' ?>\n<root />");
    assertEquals("1.1", doc.getVersion());
    assertEquals("Utf-8", doc.getEncoding());
    assertTrue(doc.isStandalone());
  }

  @Test
  public void testDocumentXMLDecl4() throws Exception {
    setUp("<?xml   version = '1.1'  encoding = 'Utf-8' standalone = 'no' ?>\n<root />");
    assertEquals("1.1", doc.getVersion());
    assertEquals("Utf-8", doc.getEncoding());
    assertFalse(doc.isStandalone());
  }

  @Test
  public void testDocumentXMLDecl5() throws Exception {
    Document doc = new Document();
    try {
      doc.addNode(new ProcessingInstruction("xml"));
      fail("No exception was thrown");
    } catch (XMLParseException e) {
      assertEquals("Line 1, column 1: Missing version attribute", e.getMessage());
    }
  }

  @Test
  public void testDocumentXMLDecl6() throws Exception {
    Document doc = new Document();
    try {
      doc.addNode(new ProcessingInstruction("xml", "encoding='utf-8'"));
      fail("No exception was thrown");
    } catch (XMLParseException e) {
      assertEquals("Line 1, column 1: Version must be before encoding", e.getMessage());
    }
  }

  @Test
  public void testDocumentXMLDecl7() throws Exception {
    Document doc = new Document();
    try {
      doc.addNode(new ProcessingInstruction("xml", "version='1.1' encoding=''"));
      fail("No exception was thrown");
    } catch (XMLParseException e) {
      assertEquals("Line 1, column 1: Value for encoding is empty", e.getMessage());
    }
  }

  @Test
  public void testDocumentXMLDecl8() throws Exception {
    Document doc = new Document();
    try {
      doc.addNode(
          new ProcessingInstruction("xml", "version='1.1' encoding='utf-8' standalone='' "));
      fail("No exception was thrown");
    } catch (XMLParseException e) {
      assertEquals("Line 1, column 1: Value for standalone is empty", e.getMessage());
    }
  }

  @Test
  public void testDocumentXMLDecl9() throws Exception {
    Document doc = new Document();
    try {
      doc.addNode(
          new ProcessingInstruction("xml", " version='1.1' encoding='utf-8' standalone='xxx' "));
      fail("No exception was thrown");
    } catch (XMLParseException e) {
      assertEquals(
          "Line 1, column 1: Allowed values for standalone are 'yes' and 'no', found 'xxx'",
          e.getMessage());
    }
  }

  @Test
  public void testDocumentXMLDecl10() throws Exception {
    Document doc = new Document();
    doc.addNode(new Element("e"));
    doc.setVersion("1.1");

    assertEquals("<?xml version=\"1.1\"?>\n<e/>", doc.toXML());
  }

  @Test
  public void testDocumentXMLDecl11() throws Exception {
    Document doc = new Document();
    doc.addNode(new Element("e"));
    doc.setEncoding(XMLInputStreamReader.ENCODING_ISO_Latin_1);

    assertEquals("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<e/>", doc.toXML());
  }

  @Test
  public void testDocumentXMLDecl12() throws Exception {
    Document doc = new Document();
    doc.addNode(new Element("e"));
    doc.setStandalone(true);

    // The spec demands that an encoding is set if standalone is specified
    // but the W3C test suite has examples which omit the encoding in this case.
    // assertEquals ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<e/>",
    // doc.toXML ());
    assertEquals("<?xml version=\"1.0\" standalone=\"yes\"?>\n<e/>", doc.toXML());
  }

  @Test
  public void testDocumentXMLDecl13() throws Exception {
    Document doc = new Document();
    doc.addNode(new Element("e"));
    doc.setStandalone(true);
    doc.setEncoding(XMLInputStreamReader.ENCODING_ISO_Latin_1);

    assertEquals(
        "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" standalone=\"yes\"?>\n<e/>", doc.toXML());
  }

  @Test
  public void testDocumentXMLDecl14() throws Exception {
    Document doc = new Document();
    doc.addNode(new Element("e"));
    doc.setEncoding(XMLInputStreamReader.ENCODING_ISO_Latin_1);

    try {
      doc.addNode(0, new Text("xxx"));
      fail("No exception was thrown");
    } catch (XMLParseException e) {
      assertEquals(
          "Line 1, column 1: It is not allowed to have content before the XML declaration",
          e.getMessage());
    }
  }

  @Test
  public void testDocumentXMLDecl15() throws Exception {
    Document doc = new Document();
    doc.addNode(new Element("e"));
    doc.setVersion(null);

    assertEquals("<?xml version=\"1.0\"?>\n<e/>", doc.toXML());
  }

  @Test
  public void testEntityResolver() throws Exception {
    XMLParser parser = new XMLParser();

    // Default is not to expand entites; in fact they are treated as text
    assertNull(parser.getEntityResolver());
    assertFalse(parser.isExpandEntities());
    assertTrue(parser.isTreatEntitiesAsText());

    parser.setEntityResolver(new HTMLEntityResolver());

    // Make sure this enables entity resolution
    assertTrue(parser.isExpandEntities());
    assertFalse(parser.isTreatEntitiesAsText());

    doc =
        parser.parse(
            new XMLStringSource("<?xml version=\"1.0\"?>\n" + "<xml>&lt;a&gt;&nbsp;</xml>\n"));

    Element root = doc.getRootElement();
    assertEquals("<a>\u00a0", root.getText());
    Text entity = (Text) root.getNode(0);
    assertEquals("&lt;", entity.getValue());
    assertEquals("<", entity.getText());
  }

  @Test
  public void testWithoutEntityResolver() throws Exception {
    XMLParser parser = new XMLParser();
    parser.setEntityResolver(new HTMLEntityResolver());
    parser.setExpandEntities(false);

    assertFalse(parser.isExpandEntities());
    assertFalse(parser.isTreatEntitiesAsText());

    doc =
        parser.parse(
            new XMLStringSource("<?xml version=\"1.0\"?>\n" + "<xml>&lt;a&gt;&nbsp;</xml>\n"));

    Element root = doc.getRootElement();
    assertEquals("<a>\u00a0", root.getText());
    Entity entity = (Entity) root.getNode(0);
    assertEquals("lt", entity.getName());
    assertEquals("&lt;", entity.getValue());
    assertEquals("<", entity.getText());

    entity = (Entity) root.getNode(3);
    assertEquals("nbsp", entity.getName());
    assertEquals("&nbsp;", entity.getValue());
    assertEquals("\u00a0", entity.getText());
  }

  private Document doc;
  private Element root;

  public void setUp(String xml) {
    XMLParser parser = new XMLParser();
    doc = parser.parse(new XMLStringSource(xml));
    root = doc.getRootElement();
  }

  @After
  public void tearDown() {
    doc = null;
    root = null;
  }
}
