4e4bff04a5727ea37f2d904f37c8cb7b3b85edf9
[profile/ivi/qtbase.git] / src / dbus / qdbusxmlgenerator.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the QtDBus module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtCore/qmetaobject.h>
43 #include <QtCore/qstringlist.h>
44
45 #include "qdbusinterface_p.h"   // for ANNOTATION_NO_WAIT
46 #include "qdbusabstractadaptor_p.h" // for QCLASSINFO_DBUS_*
47 #include "qdbusconnection_p.h"  // for the flags
48 #include "qdbusmetatype_p.h"
49 #include "qdbusmetatype.h"
50 #include "qdbusutil_p.h"
51
52 #ifndef QT_NO_DBUS
53
54 QT_BEGIN_NAMESPACE
55
56 extern Q_DBUS_EXPORT QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
57                                                        const QMetaObject *base, int flags);
58
59 static inline QString typeNameToXml(const char *typeName)
60 {
61     // ### copied from qtextdocument.cpp
62     // ### move this into QtCore at some point
63     QString plain = QLatin1String(typeName);
64     QString rich;
65     rich.reserve(int(plain.length() * 1.1));
66     for (int i = 0; i < plain.length(); ++i) {
67         if (plain.at(i) == QLatin1Char('<'))
68             rich += QLatin1String("&lt;");
69         else if (plain.at(i) == QLatin1Char('>'))
70             rich += QLatin1String("&gt;");
71         else if (plain.at(i) == QLatin1Char('&'))
72             rich += QLatin1String("&amp;");
73         else
74             rich += plain.at(i);
75     }
76     return rich;
77 }
78
79 // implement the D-Bus org.freedesktop.DBus.Introspectable interface
80 // we do that by analysing the metaObject of all the adaptor interfaces
81
82 static QString generateInterfaceXml(const QMetaObject *mo, int flags, int methodOffset, int propOffset)
83 {
84     QString retval;
85
86     // start with properties:
87     if (flags & (QDBusConnection::ExportScriptableProperties |
88                  QDBusConnection::ExportNonScriptableProperties)) {
89         for (int i = propOffset; i < mo->propertyCount(); ++i) {
90             static const char *accessvalues[] = {0, "read", "write", "readwrite"};
91
92             QMetaProperty mp = mo->property(i);
93
94             if (!((mp.isScriptable() && (flags & QDBusConnection::ExportScriptableProperties)) ||
95                   (!mp.isScriptable() && (flags & QDBusConnection::ExportNonScriptableProperties))))
96                 continue;
97
98             int access = 0;
99             if (mp.isReadable())
100                 access |= 1;
101             if (mp.isWritable())
102                 access |= 2;
103
104             int typeId = qDBusNameToTypeId(mp.typeName());
105             if (!typeId)
106                 continue;
107             const char *signature = QDBusMetaType::typeToSignature(typeId);
108             if (!signature)
109                 continue;
110
111             retval += QString::fromLatin1("    <property name=\"%1\" type=\"%2\" access=\"%3\"")
112                       .arg(QLatin1String(mp.name()))
113                       .arg(QLatin1String(signature))
114                       .arg(QLatin1String(accessvalues[access]));
115
116             if (QDBusMetaType::signatureToType(signature) == QVariant::Invalid) {
117                 const char *typeName = QVariant::typeToName(QVariant::Type(typeId));
118                 retval += QString::fromLatin1(">\n      <annotation name=\"com.trolltech.QtDBus.QtTypeName\" value=\"%3\"/>\n    </property>\n")
119                           .arg(typeNameToXml(typeName));
120             } else {
121                 retval += QLatin1String("/>\n");
122             }
123         }
124     }
125
126     // now add methods:
127     for (int i = methodOffset; i < mo->methodCount(); ++i) {
128         QMetaMethod mm = mo->method(i);
129         QByteArray signature = mm.signature();
130         int paren = signature.indexOf('(');
131
132         bool isSignal;
133         if (mm.methodType() == QMetaMethod::Signal)
134             // adding a signal
135             isSignal = true;
136         else if (mm.access() == QMetaMethod::Public && (mm.methodType() == QMetaMethod::Slot || mm.methodType() == QMetaMethod::Method))
137             isSignal = false;
138         else
139             continue;           // neither signal nor public slot
140
141         if (isSignal && !(flags & (QDBusConnection::ExportScriptableSignals |
142                                    QDBusConnection::ExportNonScriptableSignals)))
143             continue;           // we're not exporting any signals
144         if (!isSignal && (!(flags & (QDBusConnection::ExportScriptableSlots | QDBusConnection::ExportNonScriptableSlots)) &&
145                           !(flags & (QDBusConnection::ExportScriptableInvokables | QDBusConnection::ExportNonScriptableInvokables))))
146             continue;           // we're not exporting any slots or invokables
147
148         QString xml = QString::fromLatin1("    <%1 name=\"%2\">\n")
149                       .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"))
150                       .arg(QLatin1String(signature.left(paren)));
151
152         // check the return type first
153         int typeId = qDBusNameToTypeId(mm.typeName());
154         if (typeId) {
155             const char *typeName = QDBusMetaType::typeToSignature(typeId);
156             if (typeName) {
157                 xml += QString::fromLatin1("      <arg type=\"%1\" direction=\"out\"/>\n")
158                        .arg(typeNameToXml(typeName));
159
160                 // do we need to describe this argument?
161                 if (QDBusMetaType::signatureToType(typeName) == QVariant::Invalid)
162                     xml += QString::fromLatin1("      <annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"%1\"/>\n")
163                         .arg(typeNameToXml(QVariant::typeToName(QVariant::Type(typeId))));
164             } else
165                 continue;
166         }
167         else if (*mm.typeName())
168             continue;           // wasn't a valid type
169
170         QList<QByteArray> names = mm.parameterNames();
171         QList<int> types;
172         int inputCount = qDBusParametersForMethod(mm, types);
173         if (inputCount == -1)
174             continue;           // invalid form
175         if (isSignal && inputCount + 1 != types.count())
176             continue;           // signal with output arguments?
177         if (isSignal && types.at(inputCount) == QDBusMetaTypeId::message)
178             continue;           // signal with QDBusMessage argument?
179         if (isSignal && mm.attributes() & QMetaMethod::Cloned)
180             continue;           // cloned signal?
181
182         int j;
183         bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
184         for (j = 1; j < types.count(); ++j) {
185             // input parameter for a slot or output for a signal
186             if (types.at(j) == QDBusMetaTypeId::message) {
187                 isScriptable = true;
188                 continue;
189             }
190
191             QString name;
192             if (!names.at(j - 1).isEmpty())
193                 name = QString::fromLatin1("name=\"%1\" ").arg(QLatin1String(names.at(j - 1)));
194
195             bool isOutput = isSignal || j > inputCount;
196
197             const char *signature = QDBusMetaType::typeToSignature(types.at(j));
198             xml += QString::fromLatin1("      <arg %1type=\"%2\" direction=\"%3\"/>\n")
199                    .arg(name)
200                    .arg(QLatin1String(signature))
201                    .arg(isOutput ? QLatin1String("out") : QLatin1String("in"));
202
203             // do we need to describe this argument?
204             if (QDBusMetaType::signatureToType(signature) == QVariant::Invalid) {
205                 const char *typeName = QVariant::typeToName( QVariant::Type(types.at(j)) );
206                 xml += QString::fromLatin1("      <annotation name=\"com.trolltech.QtDBus.QtTypeName.%1%2\" value=\"%3\"/>\n")
207                        .arg(isOutput ? QLatin1String("Out") : QLatin1String("In"))
208                        .arg(isOutput && !isSignal ? j - inputCount : j - 1)
209                        .arg(typeNameToXml(typeName));
210             }
211         }
212
213         int wantedMask;
214         if (isScriptable)
215             wantedMask = isSignal ? QDBusConnection::ExportScriptableSignals
216                                   : QDBusConnection::ExportScriptableSlots;
217         else
218             wantedMask = isSignal ? QDBusConnection::ExportNonScriptableSignals
219                                   : QDBusConnection::ExportNonScriptableSlots;
220         if ((flags & wantedMask) != wantedMask)
221             continue;
222
223         if (qDBusCheckAsyncTag(mm.tag()))
224             // add the no-reply annotation
225             xml += QLatin1String("      <annotation name=\"" ANNOTATION_NO_WAIT "\""
226                                  " value=\"true\"/>\n");
227
228         retval += xml;
229         retval += QString::fromLatin1("    </%1>\n")
230                   .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"));
231     }
232
233     return retval;
234 }
235
236 QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
237                                    const QMetaObject *base, int flags)
238 {
239     if (interface.isEmpty())
240         // generate the interface name from the meta object
241         interface = qDBusInterfaceFromMetaObject(mo);
242
243     QString xml;
244     int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION);
245     if (idx >= mo->classInfoOffset())
246         return QString::fromUtf8(mo->classInfo(idx).value());
247     else
248         xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount());
249
250     if (xml.isEmpty())
251         return QString();       // don't add an empty interface
252     return QString::fromLatin1("  <interface name=\"%1\">\n%2  </interface>\n")
253         .arg(interface, xml);
254 }
255 #if 0
256 QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, const QMetaObject *base,
257                                    int flags)
258 {
259     if (interface.isEmpty()) {
260         // generate the interface name from the meta object
261         int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE);
262         if (idx >= mo->classInfoOffset()) {
263             interface = QLatin1String(mo->classInfo(idx).value());
264         } else {
265             interface = QLatin1String(mo->className());
266             interface.replace(QLatin1String("::"), QLatin1String("."));
267
268             if (interface.startsWith(QLatin1String("QDBus"))) {
269                 interface.prepend(QLatin1String("com.trolltech.QtDBus."));
270             } else if (interface.startsWith(QLatin1Char('Q')) &&
271                        interface.length() >= 2 && interface.at(1).isUpper()) {
272                 // assume it's Qt
273                 interface.prepend(QLatin1String("com.trolltech.Qt."));
274             } else if (!QCoreApplication::instance()||
275                        QCoreApplication::instance()->applicationName().isEmpty()) {
276                 interface.prepend(QLatin1String("local."));
277             } else {
278                 interface.prepend(QLatin1Char('.')).prepend(QCoreApplication::instance()->applicationName());
279                 QStringList domainName =
280                     QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.'),
281                                                                              QString::SkipEmptyParts);
282                 if (domainName.isEmpty())
283                     interface.prepend(QLatin1String("local."));
284                 else
285                     for (int i = 0; i < domainName.count(); ++i)
286                         interface.prepend(QLatin1Char('.')).prepend(domainName.at(i));
287             }
288         }
289     }
290
291     QString xml;
292     int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION);
293     if (idx >= mo->classInfoOffset())
294         return QString::fromUtf8(mo->classInfo(idx).value());
295     else
296         xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount());
297
298     if (xml.isEmpty())
299         return QString();       // don't add an empty interface
300     return QString::fromLatin1("  <interface name=\"%1\">\n%2  </interface>\n")
301         .arg(interface, xml);
302 }
303
304 #endif
305
306 QT_END_NAMESPACE
307
308 #endif // QT_NO_DBUS