// Copyright 2008-2014 severally by the contributors
//
// 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
//
// 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 net.sf.practicalxml.converter;

import static net.sf.practicalxml.builder.XmlBuilder.element;
import static net.sf.practicalxml.builder.XmlBuilder.text;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;

import javax.xml.namespace.QName;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import net.sf.practicalxml.DomUtil;
import net.sf.practicalxml.junit.DomAsserts;



public class TestCollectionConverter
extends AbstractConversionTestCase
{
    public TestCollectionConverter(String testName)
    {
        super(testName);
    }


//----------------------------------------------------------------------------
//  Support Code
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
//  Test Cases
//----------------------------------------------------------------------------

    public void testEmptyDom() throws Exception
    {
        Element root = element("root")
                       .toDOM().getDocumentElement();

        Map<String,?> result = CollectionConverter.convertToMap(root);
        assertEquals(0, result.size());
    }


    public void testDomWithUniqueTextChildren() throws Exception
    {
        Element root = element("root",
                            element("argle",  text("123")),
                            element("bargle", text("456")))
                       .toDOM().getDocumentElement();

        Map<String,?> result = CollectionConverter.convertToMap(root);
        assertEquals(2, result.size());
        assertEquals("123", result.get("argle"));
        assertEquals("456", result.get("bargle"));
    }


    public void testDomWithUniqueElementChildren() throws Exception
    {
        Element root = element("root",
                            element("foo",
                                element("f1", text("123")),
                                element("f2", text("456"))),
                            element("bar",
                                element("b1", text("789")),
                                element("b2", text("012")),
                                element("b3", text("345"))))
                       .toDOM().getDocumentElement();

        Map<String,?> result = CollectionConverter.convertToMap(root);
        assertEquals(2, result.size());

        Map<String,?> child1 = (Map<String,?>)result.get("foo");
        assertEquals(2, child1.size());
        assertEquals("123", child1.get("f1"));
        assertEquals("456", child1.get("f2"));

        Map<String,?> child2 = (Map<String,?>)result.get("bar");
        assertEquals(3, child2.size());
        assertEquals("789", child2.get("b1"));
        assertEquals("012", child2.get("b2"));
        assertEquals("345", child2.get("b3"));
    }


    public void testDomWithMixedRepeatedChildren() throws Exception
    {
        Element root = element("root",
                            element("argle",  text("123")),
                            element("bargle", text("456")),
                            element("argle",
                                element("foo", text("789")),
                                element("bar", text("012"))))
                       .toDOM().getDocumentElement();

        Map<String,?> result = CollectionConverter.convertToMap(root);
        assertEquals(2, result.size());
        assertEquals("456", result.get("bargle"));

        List<?> rep = (List<?>)result.get("argle");
        assertEquals(2, rep.size());
        assertEquals("123", rep.get(0));

        Map<String,?> repElem = (Map<String,?>)rep.get(1);
        assertEquals(2, repElem.size());
        assertEquals("789", repElem.get("foo"));
        assertEquals("012", repElem.get("bar"));
    }


    public void testDomWithMixedContent() throws Exception
    {
        Element root = element("root",
                            element("foo", text("123")),
                            text("baz"),
                            element("bar", text("456")))
                       .toDOM().getDocumentElement();

        Map<String,?> result = CollectionConverter.convertToMap(root);
        assertEquals(2, result.size());
        assertEquals("123", result.get("foo"));
        assertEquals("456", result.get("bar"));
    }


    public void testEmptyElementStoredAsNull() throws Exception
    {
        Element root = element("root",
                            element("foo"),
                            element("bar", text("123")))
                       .toDOM().getDocumentElement();

        Map<String,?> result = CollectionConverter.convertToMap(root);
        assertEquals(2, result.size());
        assertEquals(null, result.get("foo"));
        assertEquals("123", result.get("bar"));
    }


    public void testListOfElements() throws Exception
    {
        Element root = element("root",
                            element("foo",
                                element("bar", text("123")),
                                element("baz", text("456"))),
                            element("argle",
                                element("a1", text("bargle")),
                                element("a2", text("wargle")),
                                element("a3", text("zargle"))))
                       .toDOM().getDocumentElement();
        List<Element> children = DomUtil.getChildren(root);

        List<Map<String,?>> result = CollectionConverter.convertToMap(children);
        assertEquals(2, result.size());
    }


    public void testSingleLevelDomWithFilter() throws Exception
    {
        Element root = element("root",
                            element("argle",  text("123")),
                            element("bargle", text("456")),
                            element("wargle", text("789")))
                       .toDOM().getDocumentElement();

        Map<String,?> result = CollectionConverter.convertToMap(root, "argle", "wargle");
        assertEquals(2, result.size());
        assertEquals("123", result.get("argle"));
        assertEquals("789", result.get("wargle"));
    }


    public void testMultiLevelDomWithFilter() throws Exception
    {
        Element root = element("root",
                            element("argle",  text("123")),
                            element("bargle", text("456")),
                            element("wargle",
                                element("foo",    text("123")),
                                element("argle",  text("456")),
                                element("bar",    text("789")),
                                element("wargle", text("012"))))
                       .toDOM().getDocumentElement();

        Map<String,?> result = CollectionConverter.convertToMap(root, "argle", "wargle");
        assertEquals(2, result.size());
        assertEquals("123", result.get("argle"));

        Map<String,?> childResult = (Map<String,?>)result.get("wargle");
        assertEquals(2, childResult.size());
        assertEquals("456", childResult.get("argle"));
        assertEquals("012", childResult.get("wargle"));
    }


    public void testEmptyMap() throws Exception
    {
        Map<String,String> map = new HashMap<String,String>();

        Document dom = CollectionConverter.convertToXml(map, "root");
        DomAsserts.assertCount(0, dom, "/root/*");
    }


    public void testSingleLevelMap() throws Exception
    {
        Map<String,String> map = new HashMap<String,String>();
        map.put("foo", "123");
        map.put("bar", "456");

        Document dom = CollectionConverter.convertToXml(map, "root");
        DomAsserts.assertCount(2, dom, "/root/*");
        DomAsserts.assertEquals("123", dom, "/root/foo");
        DomAsserts.assertEquals("456", dom, "/root/bar");
    }


    public void testMultiLevelMap() throws Exception
    {
        Map<String,String> child = new HashMap<String,String>();
        child.put("foo", "123");
        child.put("bar", "456");

        Map<String,Object> parent = new HashMap<String,Object>();
        parent.put("argle", "789");
        parent.put("bargle", child);

        Document dom = CollectionConverter.convertToXml(parent, "root");
        DomAsserts.assertCount(2, dom, "/root/*");
        DomAsserts.assertEquals("789", dom, "/root/argle");
        DomAsserts.assertCount(2, dom, "/root/bargle/*");
        DomAsserts.assertEquals("123", dom, "/root/bargle/foo");
        DomAsserts.assertEquals("456", dom, "/root/bargle/bar");
    }


    public void testSingleLevelMapWithRepeatedElements() throws Exception
    {
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("foo", "123");
        map.put("baa", new String[] { "456", "789" });
        map.put("bar", Arrays.asList("012", "345"));
        map.put("baz", new TreeSet<String>(Arrays.asList("678", "901")));

        Document dom = CollectionConverter.convertToXml(map, "root");
        DomAsserts.assertCount(7, dom, "/root/*");
        DomAsserts.assertEquals("123", dom, "/root/foo");
        DomAsserts.assertEquals("456", dom, "/root/baa[1]");
        DomAsserts.assertEquals("789", dom, "/root/baa[2]");
        DomAsserts.assertEquals("012", dom, "/root/bar[1]");
        DomAsserts.assertEquals("345", dom, "/root/bar[2]");
        DomAsserts.assertEquals("678", dom, "/root/baz[1]");
        DomAsserts.assertEquals("901", dom, "/root/baz[2]");
    }

    public void testMultiLevelMapWithRepeatedElements() throws Exception
    {
        Map<String,String> child1 = new HashMap<String,String>();
        child1.put("foo", "123");
        child1.put("bar", "456");

        Map<String,String> child2 = new HashMap<String,String>();
        child2.put("foo", "789");
        child2.put("bar", "012");

        Map<String,Object> map = new HashMap<String,Object>();
        map.put("argle", Arrays.asList(child1,child2));

        Document dom = CollectionConverter.convertToXml(map, "root");
        DomAsserts.assertCount(2, dom, "/root/*");
        DomAsserts.assertEquals("123", dom, "/root/argle[1]/foo");
        DomAsserts.assertEquals("456", dom, "/root/argle[1]/bar");
        DomAsserts.assertEquals("789", dom, "/root/argle[2]/foo");
        DomAsserts.assertEquals("012", dom, "/root/argle[2]/bar");
    }


    public void testMapConvertsNullAsEmpty() throws Exception
    {
        Map<String,String> map = new HashMap<String,String>();
        map.put("foo", "123");
        map.put("bar", "456");
        map.put("baz", null);

        Document dom = CollectionConverter.convertToXml(map, "root");
        DomAsserts.assertCount(3, dom, "/root/*");
        DomAsserts.assertEquals("123", dom, "/root/foo");
        DomAsserts.assertEquals("456", dom, "/root/bar");
        DomAsserts.assertEquals("",    dom, "/root/baz");
    }


    public void testSingleLevelMapWithFilter() throws Exception
    {
        Map<String,String> map = new HashMap<String,String>();
        map.put("foo", "123");
        map.put("bar", "456");
        map.put("baz", "789");


        Document dom = CollectionConverter.convertToXml(map, "root", "foo", "baz");
        DomAsserts.assertCount(2, dom, "/root/*");
        DomAsserts.assertEquals("123", dom, "/root/foo");
        DomAsserts.assertEquals("789", dom, "/root/baz");
    }


    public void testMultiLevelMapWithFilter() throws Exception
    {
        Map<String,String> child = new HashMap<String,String>();
        child.put("foo", "123");
        child.put("bar", "456");

        Map<String,Object> parent = new HashMap<String,Object>();
        parent.put("argle", "789");
        parent.put("bar", child);

        Document dom = CollectionConverter.convertToXml(parent, "root", "bar");
        DomAsserts.assertCount(1, dom, "/root/*");
        DomAsserts.assertCount(1, dom, "/root/bar/*");
        DomAsserts.assertEquals("456", dom, "/root/bar/bar");
    }


    public void testMultiLevelMapWithNamespace() throws Exception
    {
        Map<String,String> child = new HashMap<String,String>();
        child.put("foo", "123");
        child.put("bar", "456");

        Map<String,Object> parent = new HashMap<String,Object>();
        parent.put("argle", "789");
        parent.put("bar", child);

        Document dom = CollectionConverter.convertToXml(parent, new QName("argle", "b:bargle"));

        Element root = dom.getDocumentElement();
        assertEquals("argle",  root.getNamespaceURI());
        assertEquals("b",      root.getPrefix());
        assertEquals("bargle", root.getLocalName());

        Element child1 = DomUtil.getChild(root, "argle", "argle");
        assertNotNull(child1);
        assertEquals("b", child1.getPrefix());
    }
}
