QDBusPendingCallPrivate: save 8 bytes on 64-bit archs
[profile/ivi/qtbase.git] / src / dbus / qdbusxmlgenerator.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDBus module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
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 = mp.userType();
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 = QMetaType::typeName(typeId);
118                 retval += QString::fromLatin1(">\n      <annotation name=\"org.qtproject.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
130         bool isSignal;
131         if (mm.methodType() == QMetaMethod::Signal)
132             // adding a signal
133             isSignal = true;
134         else if (mm.access() == QMetaMethod::Public && (mm.methodType() == QMetaMethod::Slot || mm.methodType() == QMetaMethod::Method))
135             isSignal = false;
136         else
137             continue;           // neither signal nor public slot
138
139         if (isSignal && !(flags & (QDBusConnection::ExportScriptableSignals |
140                                    QDBusConnection::ExportNonScriptableSignals)))
141             continue;           // we're not exporting any signals
142         if (!isSignal && (!(flags & (QDBusConnection::ExportScriptableSlots | QDBusConnection::ExportNonScriptableSlots)) &&
143                           !(flags & (QDBusConnection::ExportScriptableInvokables | QDBusConnection::ExportNonScriptableInvokables))))
144             continue;           // we're not exporting any slots or invokables
145
146         QString xml = QString::fromLatin1("    <%1 name=\"%2\">\n")
147                       .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"))
148                       .arg(QString::fromLatin1(mm.name()));
149
150         // check the return type first
151         int typeId = mm.returnType();
152         if (typeId != QMetaType::UnknownType && typeId != QMetaType::Void) {
153             const char *typeName = QDBusMetaType::typeToSignature(typeId);
154             if (typeName) {
155                 xml += QString::fromLatin1("      <arg type=\"%1\" direction=\"out\"/>\n")
156                        .arg(typeNameToXml(typeName));
157
158                 // do we need to describe this argument?
159                 if (QDBusMetaType::signatureToType(typeName) == QVariant::Invalid)
160                     xml += QString::fromLatin1("      <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"%1\"/>\n")
161                         .arg(typeNameToXml(QMetaType::typeName(typeId)));
162             } else
163                 continue;
164         }
165         else if (typeId == QMetaType::UnknownType)
166             continue;           // wasn't a valid type
167
168         QList<QByteArray> names = mm.parameterNames();
169         QVector<int> types;
170         int inputCount = qDBusParametersForMethod(mm, types);
171         if (inputCount == -1)
172             continue;           // invalid form
173         if (isSignal && inputCount + 1 != types.count())
174             continue;           // signal with output arguments?
175         if (isSignal && types.at(inputCount) == QDBusMetaTypeId::message)
176             continue;           // signal with QDBusMessage argument?
177         if (isSignal && mm.attributes() & QMetaMethod::Cloned)
178             continue;           // cloned signal?
179
180         int j;
181         bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
182         for (j = 1; j < types.count(); ++j) {
183             // input parameter for a slot or output for a signal
184             if (types.at(j) == QDBusMetaTypeId::message) {
185                 isScriptable = true;
186                 continue;
187             }
188
189             QString name;
190             if (!names.at(j - 1).isEmpty())
191                 name = QString::fromLatin1("name=\"%1\" ").arg(QLatin1String(names.at(j - 1)));
192
193             bool isOutput = isSignal || j > inputCount;
194
195             const char *signature = QDBusMetaType::typeToSignature(types.at(j));
196             xml += QString::fromLatin1("      <arg %1type=\"%2\" direction=\"%3\"/>\n")
197                    .arg(name)
198                    .arg(QLatin1String(signature))
199                    .arg(isOutput ? QLatin1String("out") : QLatin1String("in"));
200
201             // do we need to describe this argument?
202             if (QDBusMetaType::signatureToType(signature) == QVariant::Invalid) {
203                 const char *typeName = QMetaType::typeName(types.at(j));
204                 xml += QString::fromLatin1("      <annotation name=\"org.qtproject.QtDBus.QtTypeName.%1%2\" value=\"%3\"/>\n")
205                        .arg(isOutput ? QLatin1String("Out") : QLatin1String("In"))
206                        .arg(isOutput && !isSignal ? j - inputCount : j - 1)
207                        .arg(typeNameToXml(typeName));
208             }
209         }
210
211         int wantedMask;
212         if (isScriptable)
213             wantedMask = isSignal ? QDBusConnection::ExportScriptableSignals
214                                   : QDBusConnection::ExportScriptableSlots;
215         else
216             wantedMask = isSignal ? QDBusConnection::ExportNonScriptableSignals
217                                   : QDBusConnection::ExportNonScriptableSlots;
218         if ((flags & wantedMask) != wantedMask)
219             continue;
220
221         if (qDBusCheckAsyncTag(mm.tag()))
222             // add the no-reply annotation
223             xml += QLatin1String("      <annotation name=\"" ANNOTATION_NO_WAIT "\""
224                                  " value=\"true\"/>\n");
225
226         retval += xml;
227         retval += QString::fromLatin1("    </%1>\n")
228                   .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"));
229     }
230
231     return retval;
232 }
233
234 QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
235                                    const QMetaObject *base, int flags)
236 {
237     if (interface.isEmpty())
238         // generate the interface name from the meta object
239         interface = qDBusInterfaceFromMetaObject(mo);
240
241     QString xml;
242     int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION);
243     if (idx >= mo->classInfoOffset())
244         return QString::fromUtf8(mo->classInfo(idx).value());
245     else
246         xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount());
247
248     if (xml.isEmpty())
249         return QString();       // don't add an empty interface
250     return QString::fromLatin1("  <interface name=\"%1\">\n%2  </interface>\n")
251         .arg(interface, xml);
252 }
253 #if 0
254 QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, const QMetaObject *base,
255                                    int flags)
256 {
257     if (interface.isEmpty()) {
258         // generate the interface name from the meta object
259         int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE);
260         if (idx >= mo->classInfoOffset()) {
261             interface = QLatin1String(mo->classInfo(idx).value());
262         } else {
263             interface = QLatin1String(mo->className());
264             interface.replace(QLatin1String("::"), QLatin1String("."));
265
266             if (interface.startsWith(QLatin1String("QDBus"))) {
267                 interface.prepend(QLatin1String("org.qtproject.QtDBus."));
268             } else if (interface.startsWith(QLatin1Char('Q')) &&
269                        interface.length() >= 2 && interface.at(1).isUpper()) {
270                 // assume it's Qt
271                 interface.prepend(QLatin1String("org.qtproject.Qt."));
272             } else if (!QCoreApplication::instance()||
273                        QCoreApplication::instance()->applicationName().isEmpty()) {
274                 interface.prepend(QLatin1String("local."));
275             } else {
276                 interface.prepend(QLatin1Char('.')).prepend(QCoreApplication::instance()->applicationName());
277                 QStringList domainName =
278                     QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.'),
279                                                                              QString::SkipEmptyParts);
280                 if (domainName.isEmpty())
281                     interface.prepend(QLatin1String("local."));
282                 else
283                     for (int i = 0; i < domainName.count(); ++i)
284                         interface.prepend(QLatin1Char('.')).prepend(domainName.at(i));
285             }
286         }
287     }
288
289     QString xml;
290     int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION);
291     if (idx >= mo->classInfoOffset())
292         return QString::fromUtf8(mo->classInfo(idx).value());
293     else
294         xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount());
295
296     if (xml.isEmpty())
297         return QString();       // don't add an empty interface
298     return QString::fromLatin1("  <interface name=\"%1\">\n%2  </interface>\n")
299         .arg(interface, xml);
300 }
301
302 #endif
303
304 QT_END_NAMESPACE
305
306 #endif // QT_NO_DBUS