src/dbus/qdbusxmlparser.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dbus/qdbusxmlparser.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,369 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdbusxmlparser_p.h"
+#include "qdbusinterface.h"
+#include "qdbusinterface_p.h"
+#include "qdbusconnection_p.h"
+#include "qdbusutil_p.h"
+
+#include <QtXml/qdom.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qtextstream.h>
+
+QT_BEGIN_NAMESPACE
+
+static QDBusIntrospection::Annotations
+parseAnnotations(const QDomElement& elem)
+{
+    QDBusIntrospection::Annotations retval;
+    QDomNodeList list = elem.elementsByTagName(QLatin1String("annotation"));
+    for (int i = 0; i < list.count(); ++i)
+    {
+        QDomElement ann = list.item(i).toElement();
+        if (ann.isNull())
+            continue;
+
+        QString name = ann.attribute(QLatin1String("name")),
+               value = ann.attribute(QLatin1String("value"));
+
+        if (!QDBusUtil::isValidInterfaceName(name)) {
+            qWarning("Invalid D-BUS annotation '%s' found while parsing introspection",
+                     qPrintable(name));
+            continue;
+        }
+
+        retval.insert(name, value);
+    }
+
+    return retval;
+}
+
+static QDBusIntrospection::Arguments
+parseArgs(const QDomElement& elem, const QLatin1String& direction, bool acceptEmpty)
+{
+    QDBusIntrospection::Arguments retval;
+    QDomNodeList list = elem.elementsByTagName(QLatin1String("arg"));
+    for (int i = 0; i < list.count(); ++i)
+    {
+        QDomElement arg = list.item(i).toElement();
+        if (arg.isNull())
+            continue;
+
+        if ((acceptEmpty && !arg.hasAttribute(QLatin1String("direction"))) ||
+            arg.attribute(QLatin1String("direction")) == direction) {
+
+            QDBusIntrospection::Argument argData;
+            if (arg.hasAttribute(QLatin1String("name")))
+                argData.name = arg.attribute(QLatin1String("name")); // can be empty
+            argData.type = arg.attribute(QLatin1String("type"));
+            if (!QDBusUtil::isValidSingleSignature(argData.type)) {
+                qWarning("Invalid D-BUS type signature '%s' found while parsing introspection",
+                         qPrintable(argData.type));
+                continue;
+            }
+
+            retval << argData;
+        }
+    }
+    return retval;
+}
+
+QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
+                               const QString& xmlData)
+    : m_service(service), m_path(path)
+{
+    QDomDocument doc;
+    doc.setContent(xmlData);
+    m_node = doc.firstChildElement(QLatin1String("node"));
+}
+
+QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
+                               const QDomElement& node)
+    : m_service(service), m_path(path), m_node(node)
+{
+}
+
+QDBusIntrospection::Interfaces
+QDBusXmlParser::interfaces() const
+{
+    QDBusIntrospection::Interfaces retval;
+
+    if (m_node.isNull())
+        return retval;
+
+    QDomNodeList interfaceList = m_node.elementsByTagName(QLatin1String("interface"));
+    for (int i = 0; i < interfaceList.count(); ++i)
+    {
+        QDomElement iface = interfaceList.item(i).toElement();
+        QString ifaceName = iface.attribute(QLatin1String("name"));
+        if (iface.isNull())
+            continue;           // for whatever reason
+        if (!QDBusUtil::isValidInterfaceName(ifaceName)) {
+            qWarning("Invalid D-BUS interface name '%s' found while parsing introspection",
+                     qPrintable(ifaceName));
+            continue;
+        }
+
+        QDBusIntrospection::Interface *ifaceData = new QDBusIntrospection::Interface;
+        ifaceData->name = ifaceName;
+        {
+            // save the data
+            QTextStream ts(&ifaceData->introspection);
+            iface.save(ts,2);
+        }
+
+        // parse annotations
+        ifaceData->annotations = parseAnnotations(iface);
+
+        // parse methods
+        QDomNodeList list = iface.elementsByTagName(QLatin1String("method"));
+        for (int j = 0; j < list.count(); ++j)
+        {
+            QDomElement method = list.item(j).toElement();
+            QString methodName = method.attribute(QLatin1String("name"));
+            if (method.isNull())
+                continue;
+            if (!QDBusUtil::isValidMemberName(methodName)) {
+                qWarning("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
+                         qPrintable(methodName), qPrintable(ifaceName));
+                continue;
+            }
+
+            QDBusIntrospection::Method methodData;
+            methodData.name = methodName;
+
+            // parse arguments
+            methodData.inputArgs = parseArgs(method, QLatin1String("in"), true);
+            methodData.outputArgs = parseArgs(method, QLatin1String("out"), false);
+            methodData.annotations = parseAnnotations(method);
+
+            // add it
+            ifaceData->methods.insert(methodName, methodData);
+        }
+
+        // parse signals
+        list = iface.elementsByTagName(QLatin1String("signal"));
+        for (int j = 0; j < list.count(); ++j)
+        {
+            QDomElement signal = list.item(j).toElement();
+            QString signalName = signal.attribute(QLatin1String("name"));
+            if (signal.isNull())
+                continue;
+            if (!QDBusUtil::isValidMemberName(signalName)) {
+                qWarning("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
+                         qPrintable(signalName), qPrintable(ifaceName));
+                continue;
+            }
+
+            QDBusIntrospection::Signal signalData;
+            signalData.name = signalName;
+
+            // parse data
+            signalData.outputArgs = parseArgs(signal, QLatin1String("out"), true);
+            signalData.annotations = parseAnnotations(signal);
+
+            // add it
+            ifaceData->signals_.insert(signalName, signalData);
+        }
+
+        // parse properties
+        list = iface.elementsByTagName(QLatin1String("property"));
+        for (int j = 0; j < list.count(); ++j)
+        {
+            QDomElement property = list.item(j).toElement();
+            QString propertyName = property.attribute(QLatin1String("name"));
+            if (property.isNull())
+                continue;
+            if (!QDBusUtil::isValidMemberName(propertyName)) {
+                qWarning("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
+                         qPrintable(propertyName), qPrintable(ifaceName));
+                continue;
+            }
+
+            QDBusIntrospection::Property propertyData;
+
+            // parse data
+            propertyData.name = propertyName;
+            propertyData.type = property.attribute(QLatin1String("type"));
+            propertyData.annotations = parseAnnotations(property);
+
+            if (!QDBusUtil::isValidSingleSignature(propertyData.type)) {
+                // cannot be!
+                qWarning("Invalid D-BUS type signature '%s' found in property '%s.%s' while parsing introspection",
+                         qPrintable(propertyData.type), qPrintable(ifaceName),
+                         qPrintable(propertyName));
+                continue;
+            }
+
+            QString access = property.attribute(QLatin1String("access"));
+            if (access == QLatin1String("read"))
+                propertyData.access = QDBusIntrospection::Property::Read;
+            else if (access == QLatin1String("write"))
+                propertyData.access = QDBusIntrospection::Property::Write;
+            else if (access == QLatin1String("readwrite"))
+                propertyData.access = QDBusIntrospection::Property::ReadWrite;
+            else {
+                qWarning("Invalid D-BUS property access '%s' found in property '%s.%s' while parsing introspection",
+                         qPrintable(access), qPrintable(ifaceName),
+                         qPrintable(propertyName));
+                continue;       // invalid one!
+            }
+
+            // add it
+            ifaceData->properties.insert(propertyName, propertyData);
+        }
+
+        // add it
+        retval.insert(ifaceName, QSharedDataPointer<QDBusIntrospection::Interface>(ifaceData));
+    }
+
+    return retval;
+}
+
+QSharedDataPointer<QDBusIntrospection::Object>
+QDBusXmlParser::object() const
+{
+    if (m_node.isNull())
+        return QSharedDataPointer<QDBusIntrospection::Object>();
+
+    QDBusIntrospection::Object* objData;
+    objData = new QDBusIntrospection::Object;
+    objData->service = m_service;
+    objData->path = m_path;
+
+    // check if we have anything to process
+    if (objData->introspection.isNull() && !m_node.firstChild().isNull()) {
+        // yes, introspect this object
+        QTextStream ts(&objData->introspection);
+        m_node.save(ts,2);
+
+        QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node"));
+        for (int i = 0; i < objects.count(); ++i) {
+            QDomElement obj = objects.item(i).toElement();
+            QString objName = obj.attribute(QLatin1String("name"));
+            if (obj.isNull())
+                continue;           // for whatever reason
+            if (!QDBusUtil::isValidObjectPath(m_path + QLatin1Char('/') + objName)) {
+                qWarning("Invalid D-BUS object path '%s/%s' found while parsing introspection",
+                         qPrintable(m_path), qPrintable(objName));
+                continue;
+            }
+
+            objData->childObjects.append(objName);
+        }
+
+        QDomNodeList interfaceList = m_node.elementsByTagName(QLatin1String("interface"));
+        for (int i = 0; i < interfaceList.count(); ++i) {
+            QDomElement iface = interfaceList.item(i).toElement();
+            QString ifaceName = iface.attribute(QLatin1String("name"));
+            if (iface.isNull())
+                continue;
+            if (!QDBusUtil::isValidInterfaceName(ifaceName)) {
+                qWarning("Invalid D-BUS interface name '%s' found while parsing introspection",
+                         qPrintable(ifaceName));
+                continue;
+            }
+
+            objData->interfaces.append(ifaceName);
+        }
+    } else {
+        objData->introspection = QLatin1String("<node/>\n");
+    }
+
+    QSharedDataPointer<QDBusIntrospection::Object> retval;
+    retval = objData;
+    return retval;
+}
+
+QSharedDataPointer<QDBusIntrospection::ObjectTree>
+QDBusXmlParser::objectTree() const
+{
+    QSharedDataPointer<QDBusIntrospection::ObjectTree> retval;
+
+    if (m_node.isNull())
+        return retval;
+
+    retval = new QDBusIntrospection::ObjectTree;
+
+    retval->service = m_service;
+    retval->path = m_path;
+
+    QTextStream ts(&retval->introspection);
+    m_node.save(ts,2);
+
+    // interfaces are easy:
+    retval->interfaceData = interfaces();
+    retval->interfaces = retval->interfaceData.keys();
+
+    // sub-objects are slightly more difficult:
+    QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node"));
+    for (int i = 0; i < objects.count(); ++i) {
+        QDomElement obj = objects.item(i).toElement();
+        QString objName = obj.attribute(QLatin1String("name"));
+        if (obj.isNull() || objName.isEmpty())
+            continue;           // for whatever reason
+
+        // check if we have anything to process
+        if (!obj.firstChild().isNull()) {
+            // yes, introspect this object
+            QString xml;
+            QTextStream ts2(&xml);
+            obj.save(ts2,0);
+
+            // parse it
+            QString objAbsName = m_path;
+            if (!objAbsName.endsWith(QLatin1Char('/')))
+                objAbsName.append(QLatin1Char('/'));
+            objAbsName += objName;
+
+            QDBusXmlParser parser(m_service, objAbsName, obj);
+            retval->childObjectData.insert(objName, parser.objectTree());
+        }
+
+        retval->childObjects << objName;
+    }
+
+    return QSharedDataPointer<QDBusIntrospection::ObjectTree>( retval );
+}
+
+QT_END_NAMESPACE