/***************************************************************************
 *
 * Copyright (C) 2008 by Kevin Krammer <k.krammer@gmx.at>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

#include "xmlmarshaller.h"

#include <dbus/qdbusdata.h>
#include <dbus/qdbusdatalist.h>
#include <dbus/qdbusdatamap.h>
#include <dbus/qdbusobjectpath.h>
#include <dbus/qdbusvariant.h>

#include <qdom.h>
#include <qvaluelist.h>

#include <kdebug.h>

static QDomElement fromBool(bool value, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("bool");
    QDomText    text    = doc.createTextNode((value ? "true" : "false"));

    element.appendChild(text);

    return element;
}

static QDomElement fromByte(Q_UINT8 value, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("byte");
    QDomText    text    = doc.createTextNode(QString::number(value));

    element.appendChild(text);

    return element;
}

static QDomElement fromInt16(Q_INT16 value, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("int16");
    QDomText    text    = doc.createTextNode(QString::number(value));

    element.appendChild(text);

    return element;
}

static QDomElement fromUInt16(Q_UINT16 value, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("uin16");
    QDomText    text    = doc.createTextNode(QString::number(value));

    element.appendChild(text);

    return element;
}

static QDomElement fromInt32(Q_INT32 value, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("int32");
    QDomText    text    = doc.createTextNode(QString::number(value));

    element.appendChild(text);

    return element;
}

static QDomElement fromUInt32(Q_UINT32 value, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("uint32");
    QDomText    text    = doc.createTextNode(QString::number(value));

    element.appendChild(text);

    return element;
}

static QDomElement fromInt64(Q_INT64 value, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("int64");
    QDomText    text    = doc.createTextNode(QString::number(value));

    element.appendChild(text);

    return element;
}

static QDomElement fromUInt64(Q_UINT64 value, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("uint64");
    QDomText    text    = doc.createTextNode(QString::number(value));

    element.appendChild(text);

    return element;
}

static QDomElement fromDouble(double value, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("double");
    QDomText    text    = doc.createTextNode(QString::number(value));

    element.appendChild(text);

    return element;
}

static QDomElement fromString(const QString& value, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("string");
    QDomText    text    = doc.createTextNode(value); // TODO: espace

    element.appendChild(text);

    return element;
}

static QDomElement fromObjectPath(const QDBusObjectPath& value, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("objectpath");
    QDomText    text    = doc.createTextNode(value);

    element.appendChild(text);

    return element;
}

static QDomElement fromByteKeyMap(const QDBusDataMap<Q_UINT8>& map, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("map");

    if (map.isEmpty()) return element;

    QDBusDataMap<Q_UINT8>::const_iterator it    = map.begin();
    QDBusDataMap<Q_UINT8>::const_iterator endIt = map.end();
    for (; it != endIt; ++it)
    {
        QDomElement entryElement = doc.createElement("entry");

        entryElement.appendChild(fromByte(it.key(), ownerDoc));
        entryElement.appendChild(XMLMarshaller::fromQDBusData(it.data(), ownerDoc));

        element.appendChild(entryElement);
    }

    return element;
}

static QDomElement fromInt16KeyMap(const QDBusDataMap<Q_INT16>& map, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("map");

    if (map.isEmpty()) return element;

    QDBusDataMap<Q_INT16>::const_iterator it    = map.begin();
    QDBusDataMap<Q_INT16>::const_iterator endIt = map.end();
    for (; it != endIt; ++it)
    {
        QDomElement entryElement = doc.createElement("entry");

        entryElement.appendChild(fromInt16(it.key(), ownerDoc));
        entryElement.appendChild(XMLMarshaller::fromQDBusData(it.data(), ownerDoc));

        element.appendChild(entryElement);
    }

    return element;
}

static QDomElement fromUInt16KeyMap(const QDBusDataMap<Q_UINT16>& map, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("map");

    if (map.isEmpty()) return element;

    QDBusDataMap<Q_UINT16>::const_iterator it    = map.begin();
    QDBusDataMap<Q_UINT16>::const_iterator endIt = map.end();
    for (; it != endIt; ++it)
    {
        QDomElement entryElement = doc.createElement("entry");

        entryElement.appendChild(fromUInt16(it.key(), ownerDoc));
        entryElement.appendChild(XMLMarshaller::fromQDBusData(it.data(), ownerDoc));

        element.appendChild(entryElement);
    }

    return element;
}

static QDomElement fromInt32KeyMap(const QDBusDataMap<Q_INT32>& map, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("map");

    if (map.isEmpty()) return element;

    QDBusDataMap<Q_INT32>::const_iterator it    = map.begin();
    QDBusDataMap<Q_INT32>::const_iterator endIt = map.end();
    for (; it != endIt; ++it)
    {
        QDomElement entryElement = doc.createElement("entry");

        entryElement.appendChild(fromInt32(it.key(), ownerDoc));
        entryElement.appendChild(XMLMarshaller::fromQDBusData(it.data(), ownerDoc));

        element.appendChild(entryElement);
    }

    return element;
}

static QDomElement fromUInt32KeyMap(const QDBusDataMap<Q_UINT32>& map, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("map");

    if (map.isEmpty()) return element;

    QDBusDataMap<Q_UINT32>::const_iterator it    = map.begin();
    QDBusDataMap<Q_UINT32>::const_iterator endIt = map.end();
    for (; it != endIt; ++it)
    {
        QDomElement entryElement = doc.createElement("entry");

        entryElement.appendChild(fromUInt32(it.key(), ownerDoc));
        entryElement.appendChild(XMLMarshaller::fromQDBusData(it.data(), ownerDoc));

        element.appendChild(entryElement);
    }

    return element;
}

static QDomElement fromInt64KeyMap(const QDBusDataMap<Q_INT64>& map, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("map");

    if (map.isEmpty()) return element;

    QDBusDataMap<Q_INT64>::const_iterator it    = map.begin();
    QDBusDataMap<Q_INT64>::const_iterator endIt = map.end();
    for (; it != endIt; ++it)
    {
        QDomElement entryElement = doc.createElement("entry");

        entryElement.appendChild(fromInt16(it.key(), ownerDoc));
        entryElement.appendChild(XMLMarshaller::fromQDBusData(it.data(), ownerDoc));

        element.appendChild(entryElement);
    }

    return element;
}

static QDomElement fromUInt64KeyMap(const QDBusDataMap<Q_UINT64>& map, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("map");

    if (map.isEmpty()) return element;

    QDBusDataMap<Q_UINT64>::const_iterator it    = map.begin();
    QDBusDataMap<Q_UINT64>::const_iterator endIt = map.end();
    for (; it != endIt; ++it)
    {
        QDomElement entryElement = doc.createElement("entry");

        entryElement.appendChild(fromUInt64(it.key(), ownerDoc));
        entryElement.appendChild(XMLMarshaller::fromQDBusData(it.data(), ownerDoc));

        element.appendChild(entryElement);
    }

    return element;
}

static QDomElement fromStringKeyMap(const QDBusDataMap<QString>& map, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("map");

    if (map.isEmpty()) return element;

    QDBusDataMap<QString>::const_iterator it    = map.begin();
    QDBusDataMap<QString>::const_iterator endIt = map.end();
    for (; it != endIt; ++it)
    {
        QDomElement entryElement = doc.createElement("entry");

        entryElement.appendChild(fromString(it.key(), ownerDoc));
        entryElement.appendChild(XMLMarshaller::fromQDBusData(it.data(), ownerDoc));

        element.appendChild(entryElement);
    }

    return element;
}

static QDomElement fromObjectPathKeyMap(const QDBusDataMap<QDBusObjectPath>& map, const QDomDocument& ownerDoc)
{
    QDomDocument doc    = ownerDoc;
    QDomElement element = doc.createElement("map");

    if (map.isEmpty()) return element;

    QDBusDataMap<QDBusObjectPath>::const_iterator it    = map.begin();
    QDBusDataMap<QDBusObjectPath>::const_iterator endIt = map.end();
    for (; it != endIt; ++it)
    {
        QDomElement entryElement = doc.createElement("entry");

        entryElement.appendChild(fromObjectPath(it.key(), ownerDoc));
        entryElement.appendChild(XMLMarshaller::fromQDBusData(it.data(), ownerDoc));

        element.appendChild(entryElement);
    }

    return element;
}

static bool toBool(const QDomElement& element)
{
    return element.text() == "true";
}

static Q_UINT8 toByte(const QDomElement& element)
{
    uint number = element.text().toUInt();
    if (number > 255) return 0;
    return number;
}

static Q_INT16 toInt16(const QDomElement& element)
{
    return element.text().toShort();
}

static Q_UINT16 toUInt16(const QDomElement& element)
{
    return element.text().toUShort();
}

static Q_INT32 toInt32(const QDomElement& element)
{
    return element.text().toInt();
}

static Q_UINT32 toUInt32(const QDomElement& element)
{
    return element.text().toUInt();
}

static Q_INT64 toInt64(const QDomElement& element)
{
    return element.text().toLongLong();
}

static Q_UINT64 toUInt64(const QDomElement& element)
{
    return element.text().toULongLong();
}

static double toDouble(const QDomElement& element)
{
    return element.text().toDouble();
}

static QString toString(const QDomElement& element)
{
    return element.text();
}

static QDBusObjectPath toObjectPath(const QDomElement& element)
{
    return QCString(element.text().ascii());
}

static QDBusData::Type typeFromEntry(const QDomElement& element)
{
    for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
    {
        if (!child.isElement()) continue;

        QDomElement childElement = child.toElement();

        if (childElement.tagName() == "byte")       return QDBusData::Byte;
        if (childElement.tagName() == "int16")      return QDBusData::Int16;
        if (childElement.tagName() == "uint16")     return QDBusData::UInt16;
        if (childElement.tagName() == "int32")      return QDBusData::Int32;
        if (childElement.tagName() == "uint32")     return QDBusData::UInt32;
        if (childElement.tagName() == "int64")      return QDBusData::Int64;
        if (childElement.tagName() == "uint64")     return QDBusData::UInt64;
        if (childElement.tagName() == "string")     return QDBusData::String;
        if (childElement.tagName() == "objectpath") return QDBusData::ObjectPath;
    }

    return QDBusData::Invalid;
}

static QDBusData toByteKeyMap(const QDomElement& element)
{
    QDBusDataMap<Q_UINT8> map;

    for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
    {
        if (!child.isElement()) continue;

        QDomElement entryElement = child.toElement();
        if (entryElement.tagName() != "entry") continue;

        for (QDomNode entryChild = entryElement.firstChild(); !entryChild.isNull();
             entryChild = entryChild.nextSibling())
        {
            if (!entryChild.isElement()) continue;

            QDomElement childElement = entryChild.toElement();
            if (childElement.tagName() != "byte") continue;

            Q_UINT8 key = toByte(childElement);

            QDBusData data;
            for (entryChild = entryChild.nextSibling(); !entryChild.isNull();
                 entryChild = entryChild.nextSibling())
            {
                if (!entryChild.isElement()) continue;

                data = XMLMarshaller::toQDBusData(entryChild.toElement());
                if (data.isValid()) break;
            }

            if (!data.isValid()) return QDBusData();

            map.insert(key, data);
            break;
        }
    }

    return QDBusData::fromByteKeyMap(map);
}

static QDBusData toInt16KeyMap(const QDomElement& element)
{
    QDBusDataMap<Q_INT16> map;

    for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
    {
        if (!child.isElement()) continue;

        QDomElement entryElement = child.toElement();
        if (entryElement.tagName() != "entry") continue;

        for (QDomNode entryChild = entryElement.firstChild(); !entryChild.isNull();
             entryChild = entryChild.nextSibling())
        {
            if (!entryChild.isElement()) continue;

            QDomElement childElement = entryChild.toElement();
            if (childElement.tagName() != "int16") continue;

            Q_INT16 key = toInt16(childElement);

            QDBusData data;
            for (entryChild = entryChild.nextSibling(); !entryChild.isNull();
                 entryChild = entryChild.nextSibling())
            {
                if (!entryChild.isElement()) continue;

                data = XMLMarshaller::toQDBusData(entryChild.toElement());
                if (data.isValid()) break;
            }

            if (!data.isValid()) return QDBusData();

            map.insert(key, data);
            break;
        }
    }

    return QDBusData::fromInt16KeyMap(map);
}

static QDBusData toUInt16KeyMap(const QDomElement& element)
{
    QDBusDataMap<Q_UINT16> map;

    for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
    {
        if (!child.isElement()) continue;

        QDomElement entryElement = child.toElement();
        if (entryElement.tagName() != "entry") continue;

        for (QDomNode entryChild = entryElement.firstChild(); !entryChild.isNull();
             entryChild = entryChild.nextSibling())
        {
            if (!entryChild.isElement()) continue;

            QDomElement childElement = entryChild.toElement();
            if (childElement.tagName() != "uint16") continue;

            Q_UINT16 key = toUInt16(childElement);

            QDBusData data;
            for (entryChild = entryChild.nextSibling(); !entryChild.isNull();
                 entryChild = entryChild.nextSibling())
            {
                if (!entryChild.isElement()) continue;

                data = XMLMarshaller::toQDBusData(entryChild.toElement());
                if (data.isValid()) break;
            }

            if (!data.isValid()) return QDBusData();

            map.insert(key, data);
            break;
        }
    }

    return QDBusData::fromUInt16KeyMap(map);
}

static QDBusData toInt32KeyMap(const QDomElement& element)
{
    QDBusDataMap<Q_INT32> map;

    for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
    {
        if (!child.isElement()) continue;

        QDomElement entryElement = child.toElement();
        if (entryElement.tagName() != "entry") continue;

        for (QDomNode entryChild = entryElement.firstChild(); !entryChild.isNull();
             entryChild = entryChild.nextSibling())
        {
            if (!entryChild.isElement()) continue;

            QDomElement childElement = entryChild.toElement();
            if (childElement.tagName() != "int32") continue;

            Q_INT32 key = toInt32(childElement);

            QDBusData data;
            for (entryChild = entryChild.nextSibling(); !entryChild.isNull();
                 entryChild = entryChild.nextSibling())
            {
                if (!entryChild.isElement()) continue;

                data = XMLMarshaller::toQDBusData(entryChild.toElement());
                if (data.isValid()) break;
            }

            if (!data.isValid()) return QDBusData();

            map.insert(key, data);
            break;
        }
    }

    return QDBusData::fromInt32KeyMap(map);
}

static QDBusData toUInt32KeyMap(const QDomElement& element)
{
    QDBusDataMap<Q_UINT32> map;

    for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
    {
        if (!child.isElement()) continue;

        QDomElement entryElement = child.toElement();
        if (entryElement.tagName() != "entry") continue;

        for (QDomNode entryChild = entryElement.firstChild(); !entryChild.isNull();
             entryChild = entryChild.nextSibling())
        {
            if (!entryChild.isElement()) continue;

            QDomElement childElement = entryChild.toElement();
            if (childElement.tagName() != "uint32") continue;

            Q_UINT32 key = toUInt32(childElement);

            QDBusData data;
            for (entryChild = entryChild.nextSibling(); !entryChild.isNull();
                 entryChild = entryChild.nextSibling())
            {
                if (!entryChild.isElement()) continue;

                data = XMLMarshaller::toQDBusData(entryChild.toElement());
                if (data.isValid()) break;
            }

            if (!data.isValid()) return QDBusData();

            map.insert(key, data);
            break;
        }
    }

    return QDBusData::fromUInt32KeyMap(map);
}

static QDBusData toInt64KeyMap(const QDomElement& element)
{
    QDBusDataMap<Q_INT64> map;

    for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
    {
        if (!child.isElement()) continue;

        QDomElement entryElement = child.toElement();
        if (entryElement.tagName() != "entry") continue;

        for (QDomNode entryChild = entryElement.firstChild(); !entryChild.isNull();
             entryChild = entryChild.nextSibling())
        {
            if (!entryChild.isElement()) continue;

            QDomElement childElement = entryChild.toElement();
            if (childElement.tagName() != "int64") continue;

            Q_INT64 key = toInt64(childElement);

            QDBusData data;
            for (entryChild = entryChild.nextSibling(); !entryChild.isNull();
                 entryChild = entryChild.nextSibling())
            {
                if (!entryChild.isElement()) continue;

                data = XMLMarshaller::toQDBusData(entryChild.toElement());
                if (data.isValid()) break;
            }

            if (!data.isValid()) return QDBusData();

            map.insert(key, data);
            break;
        }
    }

    return QDBusData::fromInt64KeyMap(map);
}

static QDBusData toUInt64KeyMap(const QDomElement& element)
{
    QDBusDataMap<Q_UINT64> map;

    for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
    {
        if (!child.isElement()) continue;

        QDomElement entryElement = child.toElement();
        if (entryElement.tagName() != "entry") continue;

        for (QDomNode entryChild = entryElement.firstChild(); !entryChild.isNull();
             entryChild = entryChild.nextSibling())
        {
            if (!entryChild.isElement()) continue;

            QDomElement childElement = entryChild.toElement();
            if (childElement.tagName() != "uint64") continue;

            Q_UINT64 key = toUInt64(childElement);

            QDBusData data;
            for (entryChild = entryChild.nextSibling(); !entryChild.isNull();
                 entryChild = entryChild.nextSibling())
            {
                if (!entryChild.isElement()) continue;

                data = XMLMarshaller::toQDBusData(entryChild.toElement());
                if (data.isValid()) break;
            }

            if (!data.isValid()) return QDBusData();

            map.insert(key, data);
            break;
        }
    }

    return QDBusData::fromUInt64KeyMap(map);
}

static QDBusData toStringKeyMap(const QDomElement& element)
{
    QDBusDataMap<QString> map;

    for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
    {
        if (!child.isElement()) continue;

        QDomElement entryElement = child.toElement();
        if (entryElement.tagName() != "entry") continue;

        for (QDomNode entryChild = entryElement.firstChild(); !entryChild.isNull();
             entryChild = entryChild.nextSibling())
        {
            if (!entryChild.isElement()) continue;

            QDomElement childElement = entryChild.toElement();
            if (childElement.tagName() != "string") continue;

            QString key = toString(childElement);

            QDBusData data;
            for (entryChild = entryChild.nextSibling(); !entryChild.isNull();
                 entryChild = entryChild.nextSibling())
            {
                if (!entryChild.isElement()) continue;

                data = XMLMarshaller::toQDBusData(entryChild.toElement());
                if (data.isValid()) break;
            }

            if (!data.isValid()) return QDBusData();

            map.insert(key, data);
            break;
        }
    }

    return QDBusData::fromStringKeyMap(map);
}

static QDBusData toObjectPathKeyMap(const QDomElement& element)
{
    QDBusDataMap<QDBusObjectPath> map;

    for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
    {
        if (!child.isElement()) continue;

        QDomElement entryElement = child.toElement();
        if (entryElement.tagName() != "entry") continue;

        for (QDomNode entryChild = entryElement.firstChild(); !entryChild.isNull();
             entryChild = entryChild.nextSibling())
        {
            if (!entryChild.isElement()) continue;

            QDomElement childElement = entryChild.toElement();
            if (childElement.tagName() != "objectpath") continue;

            QDBusObjectPath key = toObjectPath(childElement);

            QDBusData data;
            for (entryChild = entryChild.nextSibling(); !entryChild.isNull();
                 entryChild = entryChild.nextSibling())
            {
                if (!entryChild.isElement()) continue;

                data = XMLMarshaller::toQDBusData(entryChild.toElement());
                if (data.isValid()) break;
            }

            if (!data.isValid()) return QDBusData();

            map.insert(key, data);
            break;
        }
    }

    return QDBusData::fromObjectPathKeyMap(map);
}

QDomElement XMLMarshaller::fromQDBusData(const QDBusData& data, const QDomDocument& ownerDoc)
{
    switch (data.type())
    {
        case QDBusData::Invalid:
            kdWarning() << "XMLMarsaller::fromQDBusData: data is invalid" << endl;
            return QDomElement();

        case QDBusData::Bool:
            return fromBool(data.toBool(), ownerDoc);

        case QDBusData::Byte:
            return fromByte(data.toByte(), ownerDoc);

        case QDBusData::Int16:
            return fromInt16(data.toInt16(), ownerDoc);

        case QDBusData::UInt16:
            return fromUInt16(data.toUInt16(), ownerDoc);

        case QDBusData::Int32:
            return fromInt32(data.toInt32(), ownerDoc);

        case QDBusData::UInt32:
            return fromUInt32(data.toUInt32(), ownerDoc);

        case QDBusData::Int64:
            return fromInt64(data.toInt64(), ownerDoc);

        case QDBusData::UInt64:
            return fromUInt64(data.toUInt64(), ownerDoc);

        case QDBusData::Double:
            return fromDouble(data.toDouble(), ownerDoc);

        case QDBusData::String:
            return fromString(data.toString(), ownerDoc);

        case QDBusData::ObjectPath:
            return fromObjectPath(data.toObjectPath(), ownerDoc);

        case QDBusData::List:
        {
            QDomDocument doc    = ownerDoc;
            QDomElement element = doc.createElement("list");
            QDBusDataList list  = data.toList();
            if (list.isEmpty())
            {
                element.setAttribute("signature", data.buildDBusSignature());
            }
            else
            {
                QValueList<QDBusData> items = list.toQValueList();
                QValueList<QDBusData>::const_iterator it    = items.begin();
                QValueList<QDBusData>::const_iterator endIt = items.end();
                for (; it != endIt; ++it)
                {
                    QDomElement itemElement = fromQDBusData(*it, ownerDoc);

                    if (!itemElement.isNull()) element.appendChild(itemElement);
                }
            }

            return element;
        }

        case QDBusData::Struct:
        {
            QDomDocument doc    = ownerDoc;
            QDomElement element = doc.createElement("struct");

            QValueList<QDBusData> members = data.toStruct();
            QValueList<QDBusData>::const_iterator it    = members.begin();
            QValueList<QDBusData>::const_iterator endIt = members.end();
            for (; it != endIt; ++it)
            {
                QDomElement memberElement = fromQDBusData(*it, ownerDoc);

                if (!memberElement.isNull()) element.appendChild(memberElement);
            }
            return element;
        }

        case QDBusData::Variant:
        {
            QDomDocument doc    = ownerDoc;
            QDomElement element = doc.createElement("variant");

            QDBusVariant variant = data.toVariant();

            element.setAttribute("signature", variant.signature);

            QDomElement dataElement = fromQDBusData(variant.value, ownerDoc);
            if (!dataElement.isNull()) element.appendChild(dataElement);

            return element;
        }

        case QDBusData::Map:
        {
            QDomElement mapElement;

            switch (data.keyType())
            {
                case QDBusData::Byte:
                    mapElement = fromByteKeyMap(data.toByteKeyMap(), ownerDoc);
                    break;

                case QDBusData::Int16:
                    mapElement = fromInt16KeyMap(data.toInt16KeyMap(), ownerDoc);
                    break;

                case QDBusData::UInt16:
                    mapElement = fromUInt16KeyMap(data.toUInt16KeyMap(), ownerDoc);
                    break;

                case QDBusData::Int32:
                    mapElement = fromInt32KeyMap(data.toInt32KeyMap(), ownerDoc);
                    break;

                case QDBusData::UInt32:
                    mapElement = fromUInt32KeyMap(data.toUInt32KeyMap(), ownerDoc);
                    break;

                case QDBusData::Int64:
                    mapElement = fromInt64KeyMap(data.toInt64KeyMap(), ownerDoc);
                    break;

                case QDBusData::UInt64:
                    mapElement = fromUInt64KeyMap(data.toUInt64KeyMap(), ownerDoc);
                    break;

                case QDBusData::String:
                    mapElement = fromStringKeyMap(data.toStringKeyMap(), ownerDoc);
                    break;

                case QDBusData::ObjectPath:
                    mapElement = fromObjectPathKeyMap(data.toObjectPathKeyMap(), ownerDoc);
                    break;

                default:
                    return QDomElement();
            }

            if (!mapElement.hasChildNodes())
            {
                mapElement.setAttribute("signature", data.buildDBusSignature());
            }

            return mapElement;
        }
    }

    return QDomElement();
}

QDBusData XMLMarshaller::toQDBusData(const QDomElement& element)
{
    if (element.isNull()) return QDBusData();

    if (element.tagName() == "bool")       return QDBusData::fromBool(toBool(element));
    if (element.tagName() == "byte")       return QDBusData::fromByte(toByte(element));
    if (element.tagName() == "int16")      return QDBusData::fromInt16(toInt16(element));
    if (element.tagName() == "uint16")     return QDBusData::fromUInt16(toUInt16(element));
    if (element.tagName() == "int32")      return QDBusData::fromInt32(toInt32(element));
    if (element.tagName() == "uint32")     return QDBusData::fromUInt32(toUInt32(element));
    if (element.tagName() == "int64")      return QDBusData::fromInt64(toInt64(element));
    if (element.tagName() == "uint64")     return QDBusData::fromUInt64(toUInt64(element));
    if (element.tagName() == "double")     return QDBusData::fromDouble(toDouble(element));
    if (element.tagName() == "string")     return QDBusData::fromString(toString(element));
    if (element.tagName() == "objectpath") return QDBusData::fromObjectPath(toObjectPath(element));

    if (element.tagName() == "list")
    {
        if (element.hasChildNodes())
        {
            QValueList<QDBusData> list;

            for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
            {
                if (!child.isElement()) continue;

                QDBusData itemData = toQDBusData(child.toElement());
                if (itemData.isValid()) list << itemData;
            }

            return QDBusData::fromList(list);
        }

        // TODO handle empty list with signatures as hint
    }

    if (element.tagName() == "struct")
    {
        QValueList<QDBusData> members;

        for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
        {
            if (!child.isElement()) continue;

            QDBusData memberData = toQDBusData(child.toElement());
            if (memberData.isValid()) members << memberData;
        }

        return QDBusData::fromStruct(members);
    }

    if (element.tagName() == "variant")
    {
        QDBusData data;
        for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
        {
            if (!child.isElement()) continue;

            QDBusData childData = toQDBusData(child.toElement());
            if (childData.isValid())
            {
                data = childData;
                break;
            }
        }

        if (!data.isValid()) return QDBusData();

        QDBusVariant variant;
        variant.signature = element.attribute("signature");
        variant.value     = data;

        return QDBusData::fromVariant(variant);
    }

    if (element.tagName() == "map")
    {
        QDomElement entryElement;
        for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling())
        {
            if (!child.isElement()) continue;

            QDomElement childElement = child.toElement();
            if (childElement.tagName() == "entry")
            {
                entryElement = childElement;
                break;
            }
        }

        if (entryElement.isNull())
        {
            // TODO: empty map
            return QDBusData();
        }

        switch (typeFromEntry(entryElement))
        {
            case QDBusData::Byte:
                return toByteKeyMap(element);

            case QDBusData::Int16:
                return toInt16KeyMap(element);

            case QDBusData::UInt16:
                return toUInt16KeyMap(element);

            case QDBusData::Int32:
                return toInt32KeyMap(element);

            case QDBusData::UInt32:
                return toUInt32KeyMap(element);

            case QDBusData::Int64:
                return toInt64KeyMap(element);

            case QDBusData::UInt64:
                return toUInt64KeyMap(element);

            case QDBusData::String:
                return toStringKeyMap(element);

            case QDBusData::ObjectPath:
                return toObjectPathKeyMap(element);

            default:
                return QDBusData();
        }
    }

    return QDBusData();
}

QString XMLMarshaller::fromQDBusData(const QDBusData& data)
{
    QDomDocument doc;

    QDomElement element = fromQDBusData(data, doc);
    if (element.isNull()) return QString::null;

    doc.appendChild(element);
    return doc.toString();
}

QDBusData XMLMarshaller::toQDBusData(const QString& xmlString)
{
    QDomDocument doc;

    if (!doc.setContent(xmlString)) return QDBusData();

    return toQDBusData(doc.documentElement());
}

