* dbus/dbus-sysdeps.c: Make tcp socket connection error somewhat more
[platform/upstream/dbus.git] / qt / src / qdbusxmlparser.cpp
1 /* -*- C++ -*-
2  *
3  * Copyright (C) 2005 Thiago Macieira <thiago@kde.org>
4  * Copyright (C) 2006 Trolltech AS. All rights reserved.
5  *    Author: Thiago Macieira <thiago.macieira@trolltech.com>
6  *
7  * Licensed under the Academic Free License version 2.1
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "qdbusxmlparser_p.h"
26 #include "qdbusinterface.h"
27 #include "qdbusinterface_p.h"
28 #include "qdbusconnection_p.h"
29 #include "qdbusutil.h"
30
31 #include <QtXml/qdom.h>
32 #include <QtCore/qmap.h>
33 #include <QtCore/qvariant.h>
34 #include <QtCore/qtextstream.h>
35
36 static QDBusIntrospection::Annotations
37 parseAnnotations(const QDomElement& elem)
38 {
39     QDBusIntrospection::Annotations retval;
40     QDomNodeList list = elem.elementsByTagName(QLatin1String("annotation"));
41     for (int i = 0; i < list.count(); ++i)
42     {
43         QDomElement ann = list.item(i).toElement();
44         if (ann.isNull())
45             continue;
46
47         QString name = ann.attribute(QLatin1String("name")),
48                value = ann.attribute(QLatin1String("value"));
49
50         if (name.isEmpty())
51             continue;
52
53         retval.insert(name, value);
54     }
55
56     return retval;
57 }
58
59 static QDBusIntrospection::Arguments
60 parseArgs(const QDomElement& elem, const QLatin1String& direction, bool acceptEmpty = false)
61 {
62     QDBusIntrospection::Arguments retval;
63     QDomNodeList list = elem.elementsByTagName(QLatin1String("arg"));
64     for (int i = 0; i < list.count(); ++i)
65     {
66         QDomElement arg = list.item(i).toElement();
67         if (arg.isNull())
68             continue;
69
70         if ((acceptEmpty && !arg.hasAttribute(QLatin1String("direction"))) ||
71             arg.attribute(QLatin1String("direction")) == direction) {
72
73             QDBusIntrospection::Argument argData;
74             if (arg.hasAttribute(QLatin1String("name")))
75                 argData.name = arg.attribute(QLatin1String("name")); // can be empty
76             argData.type = arg.attribute(QLatin1String("type"));
77             if (!QDBusUtil::isValidSingleSignature(argData.type))
78                 continue;
79
80             retval << argData;
81         }
82     }
83     return retval;
84 }
85
86 QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
87                                const QString& xmlData)
88     : m_service(service), m_path(path)
89 {
90     QDomDocument doc;
91     doc.setContent(xmlData);
92     m_node = doc.firstChildElement(QLatin1String("node"));
93 }
94
95 QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
96                                const QDomElement& node)
97     : m_service(service), m_path(path), m_node(node)
98 {
99 }
100
101 QDBusIntrospection::Interfaces
102 QDBusXmlParser::interfaces() const
103 {
104     QDBusIntrospection::Interfaces retval;
105
106     if (m_node.isNull())
107         return retval;
108
109     QDomNodeList interfaces = m_node.elementsByTagName(QLatin1String("interface"));
110     for (int i = 0; i < interfaces.count(); ++i)
111     {
112         QDomElement iface = interfaces.item(i).toElement();
113         QString ifaceName = iface.attribute(QLatin1String("name"));
114         if (iface.isNull() || ifaceName.isEmpty())
115             continue;           // for whatever reason
116
117         QDBusIntrospection::Interface *ifaceData = new QDBusIntrospection::Interface;
118         ifaceData->name = ifaceName;
119         {
120             // save the data
121             QTextStream ts(&ifaceData->introspection);
122             iface.save(ts,2);
123         }
124
125         // parse annotations
126         ifaceData->annotations = parseAnnotations(iface);
127
128         // parse methods
129         QDomNodeList list = iface.elementsByTagName(QLatin1String("method"));
130         for (int j = 0; j < list.count(); ++j)
131         {
132             QDomElement method = list.item(j).toElement();
133             QString methodName = method.attribute(QLatin1String("name"));
134             if (method.isNull() || methodName.isEmpty())
135                 continue;
136
137             QDBusIntrospection::Method methodData;
138             methodData.name = methodName;
139
140             // parse arguments
141             methodData.inputArgs = parseArgs(method, QLatin1String("in"));
142             methodData.outputArgs = parseArgs(method, QLatin1String("out"));
143             methodData.annotations = parseAnnotations(method);
144
145             // add it
146             ifaceData->methods.insert(methodName, methodData);
147         }
148
149         // parse signals
150         list = iface.elementsByTagName(QLatin1String("signal"));
151         for (int j = 0; j < list.count(); ++j)
152         {
153             QDomElement signal = list.item(j).toElement();
154             QString signalName = signal.attribute(QLatin1String("name"));
155             if (signal.isNull() || signalName.isEmpty())
156                 continue;
157
158             QDBusIntrospection::Signal signalData;
159             signalData.name = signalName;
160
161             // parse data
162             signalData.outputArgs = parseArgs(signal, QLatin1String("out"), true);
163             signalData.annotations = parseAnnotations(signal);
164
165             // add it
166             ifaceData->signals_.insert(signalName, signalData);
167         }
168
169         // parse properties
170         list = iface.elementsByTagName(QLatin1String("property"));
171         for (int j = 0; j < list.count(); ++j)
172         {
173             QDomElement property = list.item(j).toElement();
174             QString propertyName = property.attribute(QLatin1String("name"));
175             if (property.isNull() || propertyName.isEmpty())
176                 continue;
177
178             QDBusIntrospection::Property propertyData;
179
180             // parse data
181             propertyData.name = propertyName;
182             propertyData.type = property.attribute(QLatin1String("type"));
183             propertyData.annotations = parseAnnotations(property);
184
185             if (!QDBusUtil::isValidSingleSignature(propertyData.type))
186                 // cannot be!
187                 continue;
188
189             QString access = property.attribute(QLatin1String("access"));
190             if (access.isEmpty())
191                 // can't be empty either!
192                 continue;
193             else if (access == QLatin1String("read"))
194                 propertyData.access = QDBusIntrospection::Property::Read;
195             else if (access == QLatin1String("write"))
196                 propertyData.access = QDBusIntrospection::Property::Write;
197             else if (access == QLatin1String("readwrite"))
198                 propertyData.access = QDBusIntrospection::Property::ReadWrite;
199             else
200                 continue;       // invalid one!
201
202             // add it
203             ifaceData->properties.insert(propertyName, propertyData);
204         }
205
206         // add it
207         retval.insert(ifaceName, QSharedDataPointer<QDBusIntrospection::Interface>(ifaceData));
208     }
209
210     return retval;
211 }
212
213 QSharedDataPointer<QDBusIntrospection::Object>
214 QDBusXmlParser::object() const
215 {
216     if (m_node.isNull())
217         return QSharedDataPointer<QDBusIntrospection::Object>();
218
219     QDBusIntrospection::Object* objData;
220     objData = new QDBusIntrospection::Object;
221     objData->service = m_service;
222     objData->path = m_path;
223
224     // check if we have anything to process
225     if (objData->introspection.isNull() && !m_node.firstChild().isNull()) {
226         // yes, introspect this object
227         QTextStream ts(&objData->introspection);
228         m_node.save(ts,2);
229
230         QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node"));
231         for (int i = 0; i < objects.count(); ++i) {
232             QDomElement obj = objects.item(i).toElement();
233             QString objName = obj.attribute(QLatin1String("name"));
234             if (obj.isNull() || objName.isEmpty())
235                 continue;           // for whatever reason
236
237             objData->childObjects.append(objName);
238         }
239
240         QDomNodeList interfaces = m_node.elementsByTagName(QLatin1String("interface"));
241         for (int i = 0; i < interfaces.count(); ++i) {
242             QDomElement iface = interfaces.item(i).toElement();
243             QString ifaceName = iface.attribute(QLatin1String("name"));
244             if (iface.isNull() || ifaceName.isEmpty())
245                 continue;
246
247             objData->interfaces.append(ifaceName);
248         }
249     } else {
250         objData->introspection = QLatin1String("<node/>\n");
251     }
252
253     QSharedDataPointer<QDBusIntrospection::Object> retval;
254     retval = objData;
255     return retval;
256 }
257
258 QSharedDataPointer<QDBusIntrospection::ObjectTree>
259 QDBusXmlParser::objectTree() const
260 {
261     QSharedDataPointer<QDBusIntrospection::ObjectTree> retval;
262
263     if (m_node.isNull())
264         return retval;
265
266     retval = new QDBusIntrospection::ObjectTree;
267
268     retval->service = m_service;
269     retval->path = m_path;
270
271     QTextStream ts(&retval->introspection);
272     m_node.save(ts,2);
273
274     // interfaces are easy:
275     retval->interfaceData = interfaces();
276     retval->interfaces = retval->interfaceData.keys();
277
278     // sub-objects are slightly more difficult:
279     QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node"));
280     for (int i = 0; i < objects.count(); ++i) {
281         QDomElement obj = objects.item(i).toElement();
282         QString objName = obj.attribute(QLatin1String("name"));
283         if (obj.isNull() || objName.isEmpty())
284             continue;           // for whatever reason
285
286         // check if we have anything to process
287         if (!obj.firstChild().isNull()) {
288             // yes, introspect this object
289             QString xml;
290             QTextStream ts(&xml);
291             obj.save(ts,0);
292
293             // parse it
294             QString objAbsName = m_path;
295             if (!objAbsName.endsWith(QLatin1Char('/')))
296                 objAbsName.append(QLatin1Char('/'));
297             objAbsName += objName;
298
299             QDBusXmlParser parser(m_service, objAbsName, obj);
300             retval->childObjectData.insert(objName, parser.objectTree());
301         }
302
303         retval->childObjects << objName;
304     }
305
306     return QSharedDataPointer<QDBusIntrospection::ObjectTree>( retval );
307 }
308