/************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 *
 * Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved.
 *
 * Use is subject to license terms.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at http://www.apache.org/licenses/LICENSE-2.0. You can also
 * obtain a copy of the License at http://odftoolkit.org/docs/license.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ************************************************************************/
package org.odftoolkit.odfdom.dom;

import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;

import org.junit.Assert;
import org.junit.Test;
import org.odftoolkit.odfdom.doc.OdfDocument;
import org.odftoolkit.odfdom.pkg.OdfAlienElement;
import org.odftoolkit.odfdom.pkg.OdfFileDom;
import org.odftoolkit.odfdom.doc.OdfPresentationDocument;
import org.odftoolkit.odfdom.utils.ResourceUtilities;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class XPathTest {

	private static final Logger LOG = Logger.getLogger(XPathTest.class.getName());
	private static final String SOURCE_FILE_1 = "XPathTest-foreignPrefix.odp";
	private static final String SOURCE_FILE_2 = "XPathTest-foreignPrefix2.odp";
        private static final String SOURCE_FILE_3 = "XPathTest-duplicate-prefix.odt";
	/**
	 * 1) The first test document "XPathTest-foreignPrefix.odp" uses the prefix "daisy" instead of "office" for ODF XML elements.
	   <daisy:document-content xmlns:daisy="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="ur...
			<daisy:scripts/>
			<daisy:automatic-styles>


	 * 2) The test doc also uses multiple identical namespace prefixes with different URIs.
	 * <text:p>Slide
	 *		<draw:element1 xmlns:draw="urn://dummy-namespace-one">
	 *			<draw:element2 xmlns:draw="urn://dummy-namespace-two"
	 *				draw:attribute2="importantValue">dummy</draw:element2></draw:element1>One!!</text:p>
	 * It will be tested, if they have been renamed correctly.

	 * 3) The test doc also uses multiple different namespace prefixes with the similar URIs.
	 *    Testing getPrefixes(String URI) from the NamespaceContext interface.
	 * <text:p>Slide
	 *		<prefixOne:element1 xmlns:prefixOne="urn://some-test-odfdom-namespace">
	 *			<prefixTwo:element2 xmlns:prefixTwo="urn://some-test-odfdom-namespace"
	 *				prefixTwo:attribute2="importantValue">dummy</prefixTwo:element2></prefixOne:element1>Two!!</text:p>
	 *
	 * 4) Having element and attribute without namespace
	 * <!-- element and attribute without namespace with an attribute value with a namespace prefix as well -->
       <text:p xmlns:alien="urn://some-test-attribute-value-namespace"
	 *		foreign="alien:valueOfAlienAttribute">Some<test>good</test> Content!!</text:p>

	 */
	@Test
	public void testXPathwithAlienNodes() throws Exception {
		try {
			OdfPresentationDocument odpWithSlides = OdfPresentationDocument.loadDocument(ResourceUtilities.getAbsolutePath(SOURCE_FILE_1));
			OdfFileDom contentDom = odpWithSlides.getContentDom();

			XPath xpath = contentDom.getXPath();
			// Test scenario 1 - see comment above
			Iterator<String> prefixes = contentDom.getPrefixes("urn:oasis:names:tc:opendocument:xmlns:office:1.0");
			// The first prefix have to be "office"
			String prefix = prefixes.next();
			Assert.assertTrue(prefix.equals("office") || prefix.equals("daisy"));
			if(prefix.equals("office")){
				prefix = prefixes.next();
				Assert.assertTrue(prefix.equals("daisy"));
			}else if(prefix.equals("daisy")){
				prefix = prefixes.next();
				Assert.assertTrue(prefix.equals("office"));
			}else{
				Assert.fail();
			}
			// There should be no further prefix
			Assert.assertFalse(prefixes.hasNext());

			Iterator<String> prefixes2 = contentDom.getPrefixes("urn://some-test-odfdom-namespace");
			prefix = prefixes2.next();
			Assert.assertTrue(prefix.equals("prefixOne") || prefix.equals("prefixTwo"));
			if(prefix.equals("prefixOne")){
				prefix = prefixes2.next();
				Assert.assertTrue(prefix.equals("prefixTwo"));
			}else if(prefix.equals("prefixTwo")){
				prefix = prefixes2.next();
				Assert.assertTrue(prefix.equals("prefixOne"));
			}else{
				Assert.fail();
			}
			// There should be no further prefix
			Assert.assertFalse(prefixes.hasNext());

			Node rootNode = contentDom.getRootElement();
			odpWithSlides.save(ResourceUtilities.newTestOutputFile("XPathTest-ForeignPrefix-output.odp"));

			if (rootNode instanceof OdfAlienElement) {
				Assert.fail("The none OOO default prefix for office: was not exchanged!");
			}
			NodeList styleNameAttributes = (NodeList) xpath.evaluate(".//*[@style:name]", rootNode, XPathConstants.NODESET);
			LOG.log(Level.INFO, "Amount of style:name is {0}", styleNameAttributes.getLength());

			Assert.assertTrue(styleNameAttributes.getLength() == 11);
			// test if the identical namespace prefixes with different URI have been renamed correctly (earlier draw:element1/draw:element2).
			String attributeWithDuplicatePrefix = (String) xpath.evaluate(".//draw__1:element1/draw__2:element2/@draw__2:attribute2", rootNode, XPathConstants.STRING);
			Assert.assertTrue(attributeWithDuplicatePrefix.equals("importantValue"));
			Assert.assertEquals("urn:oasis:names:tc:opendocument:xmlns:office:1.0", xpath.getNamespaceContext().getNamespaceURI("office"));
			Assert.assertEquals("http://www.w3.org/1999/xlink", xpath.getNamespaceContext().getNamespaceURI("xlink"));

			String alienAttributeValue = (String) xpath.evaluate(".//*/@foreign", rootNode, XPathConstants.STRING);
			LOG.log(Level.INFO, "The value of the alien attribute is {0}, expected is ''alien:valueOfAlienAttribute''!", alienAttributeValue);
			Assert.assertEquals("alien:valueOfAlienAttribute", alienAttributeValue);

			String alienElementValue = (String) xpath.evaluate("//text:p/test", rootNode, XPathConstants.STRING);
			LOG.log(Level.INFO, "The value of the alien element is {0}, expected is ''good''!", alienElementValue);
			Assert.assertEquals("good", alienElementValue);
			LOG.log(Level.INFO, "Amount of @alien:foreignAttribute and @style:name is {0}", ((NodeList) xpath.evaluate(".//*[@alien:foreignAttribute or @style:name]", rootNode, XPathConstants.NODESET)).getLength());
			Assert.assertTrue("Amount of @alien:foreignAttribute and @style:name is not 13!!", ((NodeList) xpath.evaluate(".//*[@alien:foreignAttribute or @style:name]", rootNode, XPathConstants.NODESET)).getLength() == 13);

			// Test if an empty iterator is being returned for a none existing URL
			Iterator<String> prefixes3 = contentDom.getPrefixes("urn://this-prefix-does-not-exist-in-the-xml");
			Assert.assertFalse("Not used prefix returned a none-empty iterator!", prefixes3.hasNext());
		} catch (Exception e) {
			LOG.log(Level.SEVERE, e.getMessage(), e);
			Assert.fail(e.toString());
		}
	}

	/**
	 * A typical test, that deals with xlinks in SOURCE_FILE_2
	 */
	@Test
	public void testCopyForeignSlide() {
		try {
			OdfPresentationDocument targetodp = OdfPresentationDocument.loadDocument(ResourceUtilities.getAbsolutePath(SOURCE_FILE_1));
			OdfPresentationDocument sourceodp = OdfPresentationDocument.loadDocument(ResourceUtilities.getAbsolutePath(SOURCE_FILE_2));

			int slidecount = sourceodp.getSlideCount();
			for (int i = 0; i < slidecount; i++) {
				targetodp.copyForeignSlide(i, sourceodp, i);
			}
			targetodp.save(ResourceUtilities.newTestOutputFile("XPathTest-ForeignPrefix2-output.odp"));
			targetodp.close();
		} catch (Exception e) {
			LOG.log(Level.SEVERE, e.getMessage(), e);
			Assert.fail(e.toString());
		}
	}

	/**
	 * This test SHOULD fail, but it isn't. Its source code is identical to the first test in this unit test
	 * @throws Exception
	 */
	@Test
	public void testXPathIsMissingXLinkButItWillPassBecauseItTheSecondTestInThisUnitTest() throws Exception {
		try {
			OdfPresentationDocument odpWithSlides = OdfPresentationDocument.loadDocument(ResourceUtilities.getAbsolutePath(SOURCE_FILE_1));
			OdfFileDom contentDom = odpWithSlides.getContentDom();
			XPath xpath = contentDom.getXPath();

			Node node = odpWithSlides.getContentDom().getRootElement();
			NodeList linkNodes = (NodeList) xpath.evaluate(".//*[@xlink:href]", node, XPathConstants.NODESET);
			Assert.assertNotNull(linkNodes);
			Assert.assertEquals("urn:oasis:names:tc:opendocument:xmlns:office:1.0", xpath.getNamespaceContext().getNamespaceURI("office"));
			Assert.assertEquals("http://www.w3.org/1999/xlink", xpath.getNamespaceContext().getNamespaceURI("xlink"));

		} catch (Exception e) {
			LOG.log(Level.SEVERE, e.getMessage(), e);
			Assert.fail(e.toString());
		}
	}

        /**
         * This test checks if the XPath returned by OdfFileDom is correctly aware of namespacess.
         * The NamespaceContext implementation in OdfFileDom is aware of duplicate prefixes, but getNamespaceURI(prefix)
         * in OdfFileDom does not implement it correctly
         *
         * With the patch applied in OdfFileDom these tests pass correctly
         *
         * sample metadata rdf generate from Openoffice 3.3 looks like this :
         * <?xml version="1.0" encoding="utf-8"?>
         *   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
         *     <rdf:Description rdf:about="../content.xml#id1366098766">
         *       <ns1:BungeniActionEvent xmlns:ns1="http://editor.bungeni.org/1.0/anx/"></ns1:BungeniActionEvent>
         *       <ns2:BungeniSectionID xmlns:ns2="http://editor.bungeni.org/1.0/anx/">Xda+5VC/SQKKG7Bk83a2JA</ns2:BungeniSectionID>
         *       <ns3:BungeniSectionType xmlns:ns3="http://editor.bungeni.org/1.0/anx/">Conclusion</ns3:BungeniSectionType>
         *       <ns4:hiddenBungeniMetaEditable xmlns:ns4="http://editor.bungeni.org/1.0/anx/">false</ns4:hiddenBungeniMetaEditable>
         *     </rdf:Description>
         *
         * @throws Exception
         */
        @Test
        public void testXPathDuplicatePrefixForForeignNamespace() throws Exception{
           try {

            OdfDocument odfDoc = OdfDocument.loadDocument(ResourceUtilities.getAbsolutePath(SOURCE_FILE_3));
            OdfFileDom fileDom = odfDoc.getFileDom("meta/meta.rdf");

            // add additional duplicate NS prefixes to the DOM
            fileDom.setNamespace("anx", "http://editor.bungeni.org/1.0/anx/");
            fileDom.setNamespace("myrdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");

            //get the XPath from the file dom and set its NS context to the OdfFileDom object
            XPath xpathFileDom = fileDom.getXPath();
            xpathFileDom.setNamespaceContext(fileDom);

            //PASS - this works correctly
            String rdfNsUri = fileDom.getNamespaceURI("rdf");
            LOG.log(Level.INFO, "The value of the nsuri is {0} expected ns-uri is ''http://www.w3.org/1999/02/22-rdf-syntax-ns#''", rdfNsUri);
            Assert.assertEquals("http://www.w3.org/1999/02/22-rdf-syntax-ns#", rdfNsUri);

            //FAIL - for duplicate RDF ns prefix
            String myrdfNsUri = fileDom.getNamespaceURI("myrdf");
            LOG.log(Level.INFO, "The value of the nsuri is {0} expected ns-uri is ''http://www.w3.org/1999/02/22-rdf-syntax-ns#''", myrdfNsUri);
            Assert.assertEquals("http://www.w3.org/1999/02/22-rdf-syntax-ns#", myrdfNsUri);

            //PASS - for custom NS prefix in document, only the first one
            String anxNsUri1 = fileDom.getNamespaceURI("ns1");
            LOG.log(Level.INFO, "The value of the nsuri is {0} expected ns-uri is ''http://editor.bungeni.org/1.0/anx/''", anxNsUri1);
            Assert.assertEquals("http://editor.bungeni.org/1.0/anx/", anxNsUri1);

            //FAIL - for custom NS prefix in document ns3, ns4, ns...
            String anxNsUri2 = fileDom.getNamespaceURI("ns2");
            LOG.log(Level.INFO, "The value of the ns-uri is {0} expected ns-uri is ''http://editor.bungeni.org/1.0/anx/''", anxNsUri2);
            Assert.assertEquals("http://editor.bungeni.org/1.0/anx/", anxNsUri2);

            //FAIL - for custom duplicate NS prefix anx
            String anxNsUriCustom = fileDom.getNamespaceURI("anx");
            LOG.log(Level.INFO, "The value of the ns-uri is {0} expected ns-uri is ''http://editor.bungeni.org/1.0/anx/''", anxNsUriCustom);
            Assert.assertEquals("http://editor.bungeni.org/1.0/anx/", anxNsUriCustom);

           } catch (Exception e) {
			LOG.log(Level.SEVERE, e.getMessage(), e);
			Assert.fail(e.toString());
           }
        }
}
