* qt/: Update to Subversion r548032.
[platform/upstream/dbus.git] / qt / src / qdbusxmlgenerator.cpp
1 /* -*- mode: C++ -*-
2  *
3  * Copyright (C) 2006 Trolltech AS. All rights reserved.
4  *    Author: Thiago Macieira <thiago.macieira@trolltech.com>
5  *
6  * Licensed under the Academic Free License version 2.1
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  */
23
24 #include <QtCore/qcoreapplication.h>
25 #include <QtCore/qmetaobject.h>
26 #include <QtCore/qstringlist.h>
27
28 #include "qdbusinterface_p.h"   // for ANNOTATION_NO_WAIT
29 #include "qdbusabstractadaptor_p.h" // for QCLASSINFO_DBUS_*
30 #include "qdbusconnection_p.h"  // for the flags
31 #include "qdbusutil.h"
32
33 extern QDBUS_EXPORT QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
34                                                        const QMetaObject *base, int flags);
35
36 // implement the D-Bus org.freedesktop.DBus.Introspectable interface
37 // we do that by analysing the metaObject of all the adaptor interfaces
38
39 static QString generateInterfaceXml(const QMetaObject *mo, int flags, int methodOffset, int propOffset)
40 {
41     QString retval;
42
43     // start with properties:
44     if (flags & QDBusConnection::ExportProperties) {
45         for (int i = propOffset; i < mo->propertyCount(); ++i) {
46             static const char *accessvalues[] = {0, "read", "write", "readwrite"};
47
48             QMetaProperty mp = mo->property(i);
49
50             if (!mp.isScriptable() && (flags & QDBusConnection::ExportAllProperties) !=
51                 QDBusConnection::ExportAllProperties)
52                 continue;
53
54             int access = 0;
55             if (mp.isReadable())
56                 access |= 1;
57             if (mp.isWritable())
58                 access |= 2;
59
60             int typeId = qDBusNameToTypeId(mp.typeName());
61             if (!typeId)
62                 continue;
63
64             retval += QString(QLatin1String("    <property name=\"%1\" type=\"%2\" access=\"%3\" />\n"))
65                       .arg(mp.name())
66                       .arg(QLatin1String( QDBusUtil::typeToSignature( QVariant::Type(typeId) )))
67                       .arg(QLatin1String( accessvalues[access] ));
68         }
69     }
70
71     // now add methods:
72     for (int i = methodOffset; i < mo->methodCount(); ++i) {
73         QMetaMethod mm = mo->method(i);
74         QByteArray signature = mm.signature();
75         int paren = signature.indexOf('(');
76
77         bool isSignal;
78         if (mm.methodType() == QMetaMethod::Signal)
79             // adding a signal
80             isSignal = true;
81         else if (mm.methodType() == QMetaMethod::Slot && mm.access() == QMetaMethod::Public)
82             isSignal = false;
83         else
84             continue;           // neither signal nor public slot
85
86         if ((isSignal && !(flags & QDBusConnection::ExportSignals)) ||
87             (!isSignal && !(flags & QDBusConnection::ExportSlots)))
88             continue;
89
90         QString xml = QString(QLatin1String("    <%1 name=\"%2\">\n"))
91                       .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"))
92                       .arg(QLatin1String(signature.left(paren)));
93
94         // check the return type first
95         int typeId = qDBusNameToTypeId(mm.typeName());
96         if (typeId)
97             xml += QString(QLatin1String("      <arg type=\"%1\" direction=\"out\"/>\n"))
98                    .arg(QLatin1String(QDBusUtil::typeToSignature( QVariant::Type(typeId) )));
99         else if (*mm.typeName())
100             continue;           // wasn't a valid type
101
102         QList<QByteArray> names = mm.parameterNames();
103         QList<int> types;
104         int inputCount = qDBusParametersForMethod(mm, types);
105         if (inputCount == -1)
106             continue;           // invalid form
107         if (isSignal && inputCount + 1 != types.count())
108             continue;           // signal with output arguments?
109         if (isSignal && types.at(inputCount) == QDBusConnectionPrivate::messageMetaType)
110             continue;           // signal with QDBusMessage argument?
111
112         int j;
113         bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
114         for (j = 1; j < types.count(); ++j) {
115             // input parameter for a slot or output for a signal
116             if (types.at(j) == QDBusConnectionPrivate::messageMetaType) {
117                 isScriptable = true;
118                 continue;
119             }
120
121             QString name;
122             if (!names.at(j - 1).isEmpty())
123                 name = QString(QLatin1String("name=\"%1\" ")).arg(QLatin1String(names.at(j - 1)));
124
125             bool isOutput = isSignal || j > inputCount;
126
127             xml += QString(QLatin1String("      <arg %1type=\"%2\" direction=\"%3\"/>\n"))
128                    .arg(name)
129                    .arg(QLatin1String(QDBusUtil::typeToSignature( QVariant::Type(types.at(j)) )))
130                    .arg(isOutput ? QLatin1String("out") : QLatin1String("in"));
131         }
132
133         if (!isScriptable) {
134             // check if this was added by other means
135             if (isSignal && (flags & QDBusConnection::ExportAllSignals) != QDBusConnection::ExportAllSignals)
136                 continue;
137             if (!isSignal && (flags & QDBusConnection::ExportAllSlots) != QDBusConnection::ExportAllSlots)
138                 continue;
139         }
140
141         if (qDBusCheckAsyncTag(mm.tag()))
142             // add the no-reply annotation
143             xml += QLatin1String("      <annotation name=\"" ANNOTATION_NO_WAIT "\""
144                                  " value=\"true\"/>\n");
145
146         retval += xml;
147         retval += QString(QLatin1String("    </%1>\n"))
148                   .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"));
149     }
150
151     return retval;
152 }
153
154 QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, const QMetaObject *base,
155                                    int flags)
156 {
157     if (interface.isEmpty()) {
158         // generate the interface name from the meta object
159         int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE);
160         if (idx >= mo->classInfoOffset()) {
161             interface = QLatin1String(mo->classInfo(idx).value());
162         } else {
163             interface = QLatin1String(mo->className());
164             interface.replace(QLatin1String("::"), QLatin1String("."));
165
166             if (interface.startsWith( QLatin1String("QDBus") )) {
167                 interface.prepend( QLatin1String("com.trolltech.QtDBus.") );
168             } else if (interface.startsWith( QLatin1Char('Q') )) {
169                 // assume it's Qt
170                 interface.prepend( QLatin1String("com.trolltech.Qt.") );
171             } else if (!QCoreApplication::instance() ||
172                        QCoreApplication::instance()->applicationName().isEmpty()) {
173                 interface.prepend( QLatin1String("local.") );
174             } else {
175                 interface.prepend(QLatin1Char('.')).prepend( QCoreApplication::instance()->applicationName() );
176                 QStringList domainName = QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.'));
177                 foreach (const QString &part, domainName)
178                     interface.prepend(QLatin1Char('.')).prepend(part);
179             }
180         }
181     }
182
183     QString xml;
184     int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION);
185     if (idx >= mo->classInfoOffset())
186         return QString::fromUtf8(mo->classInfo(idx).value());
187     else
188         xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount());
189
190     if (xml.isEmpty())
191         return QString();       // don't add an empty interface
192     return QString(QLatin1String("  <interface name=\"%1\">\n%2  </interface>\n"))
193         .arg(interface, xml);
194 }