1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "qdbusmodel.h"
30
31#include <QtCore/qvector.h>
32#include <QtCore/QDebug>
33#include <QtXml/QDomDocument>
34#include <QtDBus/QDBusObjectPath>
35#include <QtDBus/QDBusInterface>
36#include <QtDBus/QDBusReply>
37
38struct QDBusItem
39{
40 inline QDBusItem(QDBusModel::Type aType, const QString &aName, QDBusItem *aParent = 0)
41 : type(aType), parent(aParent), isPrefetched(type != QDBusModel::PathItem), name(aName)
42 {}
43 inline ~QDBusItem()
44 {
45 qDeleteAll(c: children);
46 }
47
48 QString path() const
49 {
50 Q_ASSERT(type == QDBusModel::PathItem);
51
52 QString s;
53 const QDBusItem *item = this;
54 while (item) {
55 s.prepend(s: item->name);
56 item = item->parent;
57 }
58 if (s.length() > 1)
59 s.chop(n: 1); // remove tailing slash
60 return s;
61 }
62
63 QDBusModel::Type type;
64 QDBusItem *parent;
65 QVector<QDBusItem *> children;
66 bool isPrefetched;
67 QString name;
68 QString caption;
69 QString typeSignature;
70};
71
72QDomDocument QDBusModel::introspect(const QString &path)
73{
74 QDomDocument doc;
75
76 QDBusInterface iface(service, path, QLatin1String("org.freedesktop.DBus.Introspectable"), c);
77 if (!iface.isValid()) {
78 QDBusError err(iface.lastError());
79 emit busError(text: QString::fromLatin1(str: "Cannot introspect object %1 at %2:\n %3 (%4)\n").arg(a: path).arg(
80 a: service).arg(a: err.name()).arg(a: err.message()));
81 return doc;
82 }
83
84 QDBusReply<QString> xml = iface.call(method: QLatin1String("Introspect"));
85
86 if (!xml.isValid()) {
87 QDBusError err(xml.error());
88 if (err.isValid()) {
89 emit busError(text: QString::fromLatin1(str: "Call to object %1 at %2:\n %3 (%4) failed\n").arg(
90 a: path).arg(a: service).arg(a: err.name()).arg(a: err.message()));
91 } else {
92 emit busError(text: QString::fromLatin1(str: "Invalid XML received from object %1 at %2\n").arg(
93 a: path).arg(a: service));
94 }
95 return doc;
96 }
97
98 doc.setContent(text: xml);
99 return doc;
100}
101
102void QDBusModel::addMethods(QDBusItem *parent, const QDomElement &iface)
103{
104 Q_ASSERT(parent);
105
106 QDomElement child = iface.firstChildElement();
107 while (!child.isNull()) {
108 QDBusItem *item = 0;
109 if (child.tagName() == QLatin1String("method")) {
110 item = new QDBusItem(QDBusModel::MethodItem,
111 child.attribute(name: QLatin1String("name")), parent);
112 item->caption = QLatin1String("Method: ") + item->name;
113 //get "type" from <arg> where "direction" is "in"
114 QDomElement n = child.firstChildElement();
115 while (!n.isNull()) {
116 if (n.attribute(name: QLatin1String("direction")) == QLatin1String("in"))
117 item->typeSignature += n.attribute(name: QLatin1String("type"));
118 n = n.nextSiblingElement();
119 }
120 } else if (child.tagName() == QLatin1String("signal")) {
121 item = new QDBusItem(QDBusModel::SignalItem,
122 child.attribute(name: QLatin1String("name")), parent);
123 item->caption = QLatin1String("Signal: ") + item->name;
124 } else if (child.tagName() == QLatin1String("property")) {
125 item = new QDBusItem(QDBusModel::PropertyItem,
126 child.attribute(name: QLatin1String("name")), parent);
127 item->caption = QLatin1String("Property: ") + item->name;
128 } else {
129 qDebug() << "addMethods: unknown tag:" << child.tagName();
130 }
131 if (item)
132 parent->children.append(t: item);
133
134 child = child.nextSiblingElement();
135 }
136}
137
138void QDBusModel::addPath(QDBusItem *parent)
139{
140 Q_ASSERT(parent);
141
142 QString path = parent->path();
143
144 QDomDocument doc = introspect(path);
145 QDomElement node = doc.documentElement();
146 QDomElement child = node.firstChildElement();
147 while (!child.isNull()) {
148 if (child.tagName() == QLatin1String("node")) {
149 QDBusItem *item = new QDBusItem(QDBusModel::PathItem,
150 child.attribute(name: QLatin1String("name")) + QLatin1Char('/'), parent);
151 parent->children.append(t: item);
152
153 addMethods(parent: item, iface: child);
154 } else if (child.tagName() == QLatin1String("interface")) {
155 QDBusItem *item = new QDBusItem(QDBusModel::InterfaceItem,
156 child.attribute(name: QLatin1String("name")), parent);
157 parent->children.append(t: item);
158
159 addMethods(parent: item, iface: child);
160 } else {
161 qDebug() << "addPath: Unknown tag name:" << child.tagName();
162 }
163 child = child.nextSiblingElement();
164 }
165
166 parent->isPrefetched = true;
167}
168
169QDBusModel::QDBusModel(const QString &aService, const QDBusConnection &connection)
170 : service(aService), c(connection), root(0)
171{
172 root = new QDBusItem(QDBusModel::PathItem, QLatin1String("/"));
173}
174
175QDBusModel::~QDBusModel()
176{
177 delete root;
178}
179
180QModelIndex QDBusModel::index(int row, int column, const QModelIndex &parent) const
181{
182 const QDBusItem *item = static_cast<QDBusItem *>(parent.internalPointer());
183 if (!item)
184 item = root;
185
186 if (column != 0 || row < 0 || row >= item->children.count())
187 return QModelIndex();
188
189 return createIndex(arow: row, acolumn: 0, adata: item->children.at(i: row));
190}
191
192QModelIndex QDBusModel::parent(const QModelIndex &child) const
193{
194 QDBusItem *item = static_cast<QDBusItem *>(child.internalPointer());
195 if (!item || !item->parent || !item->parent->parent)
196 return QModelIndex();
197
198 return createIndex(arow: item->parent->parent->children.indexOf(t: item->parent), acolumn: 0, adata: item->parent);
199}
200
201int QDBusModel::rowCount(const QModelIndex &parent) const
202{
203 QDBusItem *item = static_cast<QDBusItem *>(parent.internalPointer());
204 if (!item)
205 item = root;
206 if (!item->isPrefetched)
207 const_cast<QDBusModel *>(this)->addPath(parent: item);
208
209 return item->children.count();
210}
211
212int QDBusModel::columnCount(const QModelIndex &) const
213{
214 return 1;
215}
216
217QVariant QDBusModel::data(const QModelIndex &index, int role) const
218{
219 const QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
220 if (!item)
221 return QVariant();
222
223 if (role != Qt::DisplayRole)
224 return QVariant();
225
226 return item->caption.isEmpty() ? item->name : item->caption;
227}
228
229QVariant QDBusModel::headerData(int section, Qt::Orientation orientation, int role) const
230{
231 if (role != Qt::DisplayRole || orientation == Qt::Vertical || section != 0)
232 return QVariant();
233
234 return QLatin1String("Methods");
235}
236
237QDBusModel::Type QDBusModel::itemType(const QModelIndex &index) const
238{
239 const QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
240 return item ? item->type : PathItem;
241}
242
243void QDBusModel::refresh(const QModelIndex &aIndex)
244{
245 QModelIndex index = aIndex;
246 while (index.isValid() && static_cast<QDBusItem *>(index.internalPointer())->type != PathItem) {
247 index = index.parent();
248 }
249
250 QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
251 if (!item)
252 item = root;
253
254 if (!item->children.isEmpty()) {
255 beginRemoveRows(parent: index, first: 0, last: item->children.count() - 1);
256 qDeleteAll(c: item->children);
257 item->children.clear();
258 endRemoveRows();
259 }
260
261 addPath(parent: item);
262 if (!item->children.isEmpty()) {
263 beginInsertRows(parent: index, first: 0, last: item->children.count() - 1);
264 endInsertRows();
265 }
266}
267
268QString QDBusModel::dBusPath(const QModelIndex &aIndex) const
269{
270 QModelIndex index = aIndex;
271 while (index.isValid() && static_cast<QDBusItem *>(index.internalPointer())->type != PathItem) {
272 index = index.parent();
273 }
274
275 QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
276 if (!item)
277 item = root;
278
279 return item->path();
280}
281
282QString QDBusModel::dBusInterface(const QModelIndex &index) const
283{
284 QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
285 if (!item)
286 return QString();
287 if (item->type == InterfaceItem)
288 return item->name;
289 if (item->parent && item->parent->type == InterfaceItem)
290 return item->parent->name;
291 return QString();
292}
293
294QString QDBusModel::dBusMethodName(const QModelIndex &index) const
295{
296 QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
297 return item ? item->name : QString();
298}
299
300QString QDBusModel::dBusTypeSignature(const QModelIndex &index) const
301{
302 QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
303 return item ? item->typeSignature : QString();
304}
305
306QModelIndex QDBusModel::findObject(const QDBusObjectPath &objectPath)
307{
308 QStringList path = objectPath.path().split(sep: QLatin1Char('/'), behavior: Qt::SkipEmptyParts);
309
310 QDBusItem *item = root;
311 int childIdx = -1;
312 while (item && !path.isEmpty()) {
313 const QString branch = path.takeFirst() + QLatin1Char('/');
314 childIdx = -1;
315
316 // do a linear search over all the children
317 for (int i = 0; i < item->children.count(); ++i) {
318 QDBusItem *child = item->children.at(i);
319 if (child->type == PathItem && child->name == branch) {
320 item = child;
321 childIdx = i;
322
323 // prefetch the found branch
324 if (!item->isPrefetched)
325 addPath(parent: item);
326 break;
327 }
328 }
329
330 // branch not found - bail out
331 if (childIdx == -1)
332 return QModelIndex();
333 }
334
335 // found the right item
336 if (childIdx != -1 && item && path.isEmpty())
337 return createIndex(arow: childIdx, acolumn: 0, adata: item);
338
339 return QModelIndex();
340}
341
342

source code of qttools/src/qdbus/qdbusviewer/qdbusmodel.cpp