1
/*  This file is part of the KDE project.
2
3
Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
5
This library is free software: you can redistribute it and/or modify
6
it under the terms of the GNU Lesser General Public License as published by
7
the Free Software Foundation, either version 2.1 or 3 of the License.
8
9
This library is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
GNU Lesser General Public License for more details.
13
14
You should have received a copy of the GNU Lesser General Public License
15
along with this library.  If not, see <http://www.gnu.org/licenses/>.
16
17
*/
18
19
#include <QByteArray>
20
#include <QDebug>
21
#include <QHash>
22
#include <QTextStream>
23
#include <QWidget>
24
25
#include "objectdump.h"
26
#include "objecttree.h"
27
28
QT_BEGIN_NAMESPACE
29
30
namespace ObjectDump
31
{
32
33
//-----------------------------------------------------------------------------
34
// QObjectAnnotator
35
//-----------------------------------------------------------------------------
36
37
QAnnotator::~QAnnotator()
38
{
39
40
}
41
42
43
//-----------------------------------------------------------------------------
44
// Annotators
45
//-----------------------------------------------------------------------------
46
47
QList<QByteArray> QAnnotatorBasic::annotation(const QObject& object)
48
{
49
    QList<QByteArray> result;
50
51
    QByteArray array;
52
    QTextStream stream(&array);
53
54
    stream << '[' << &object << ']';
55
    stream << ' ';
56
    stream << object.metaObject()->className();
57
58
    if (object.objectName() != "")
59
        stream << " \"" << object.objectName() << '"';
60
61
    if (object.isWidgetType())
62
        stream << " isWidget";
63
64
    stream.flush();
65
    result.append(array);
66
    return result;
67
}
68
69
QList<QByteArray> QAnnotatorWidget::annotation(const QObject& object)
70
{
71
    QList<QByteArray> result;
72
73
    const QWidget* widget = qobject_cast<const QWidget*>(&object);
74
    if (widget) {
75
76
        QByteArray array;
77
        QTextStream stream(&array);
78
79
        stream << "widget: ";
80
81
        if (widget->isVisible())
82
            stream << "visible ";
83
        else
84
            stream << "invisible ";
85
86
        stream << widget->x() << ',' << widget->y() << ' ';
87
        stream << widget->size().width() << 'x'<< widget->size().height() << ' ';
88
89
        stream << "hint " << widget->sizeHint().width() << 'x' << widget->sizeHint().height();
90
91
        stream.flush();
92
        result.append(array);
93
    }
94
95
    return result;
96
}
97
98
99
//-----------------------------------------------------------------------------
100
// Base class for QDumperPrivate, QVisitorPrivate
101
//-----------------------------------------------------------------------------
102
103
class QDumperBase
104
{
105
public:
106
    QDumperBase();
107
    ~QDumperBase();
108
109
    void setPrefix(const QString& prefix);
110
    void addAnnotator(QAnnotator* annotator);
111
112
protected:
113
    QByteArray m_prefix;
114
    QList<QAnnotator*> m_annotators;
115
116
};
117
118
QDumperBase::QDumperBase()
119
{
120
121
}
122
123
QDumperBase::~QDumperBase()
124
{
125
    QAnnotator* annotator;
126
    foreach(annotator, m_annotators)
127
        delete annotator;
128
}
129
130
void QDumperBase::setPrefix(const QString& prefix)
131
{
132
    m_prefix = prefix.count()
133
        ? (prefix + " ").toAscii()
134
        : prefix.toAscii();
135
}
136
137
void QDumperBase::addAnnotator(QAnnotator* annotator)
138
{
139
    // Protect against an exception occurring during QList::append
140
    QScopedPointer<QAnnotator> holder(annotator);
141
    m_annotators.append(annotator);
142
    holder.take();
143
}
144
145
146
//-----------------------------------------------------------------------------
147
// QDumper
148
//-----------------------------------------------------------------------------
149
150
class QDumperPrivate : public QDumperBase
151
{
152
public:
153
    QDumperPrivate();
154
    ~QDumperPrivate();
155
156
    void dumpObject(const QObject& object);
157
158
};
159
160
161
QDumperPrivate::QDumperPrivate()
162
{
163
164
}
165
166
QDumperPrivate::~QDumperPrivate()
167
{
168
169
}
170
171
void QDumperPrivate::dumpObject(const QObject& object)
172
{
173
    QAnnotator* annotator;
174
    foreach(annotator, m_annotators) {
175
176
        const QList<QByteArray> annotations = annotator->annotation(object);
177
        QByteArray annotation;
178
        foreach(annotation, annotations) {
179
            QByteArray buffer(m_prefix);
180
            buffer.append(annotation);
181
            qDebug() << buffer.constData();
182
        }
183
    }
184
}
185
186
187
QDumper::QDumper()
188
    : d_ptr(new QDumperPrivate)
189
{
190
191
}
192
193
QDumper::~QDumper()
194
{
195
196
}
197
198
void QDumper::setPrefix(const QString& prefix)
199
{
200
    d_func()->setPrefix(prefix);
201
}
202
203
void QDumper::addAnnotator(QAnnotator* annotator)
204
{
205
    d_func()->addAnnotator(annotator);
206
}
207
208
void QDumper::dumpObject(const QObject& object)
209
{
210
    d_func()->dumpObject(object);
211
}
212
213
214
//-----------------------------------------------------------------------------
215
// QVisitor
216
//-----------------------------------------------------------------------------
217
218
class QVisitorPrivate : public QDumperBase
219
{
220
public:
221
    QVisitorPrivate();
222
    ~QVisitorPrivate();
223
224
    void setIndent(unsigned indent);
225
226
    void visitNode(const QObject& object);
227
    void visitComplete();
228
229
private:
230
    class Node
231
    {
232
    public:
233
        Node();
234
        ~Node();
235
236
        QList<QByteArray>   m_annotation;
237
        QList<Node*>        m_children;
238
239
        typedef QList<Node*>::const_iterator child_iterator;
240
    };
241
242
private:
243
    Node* findNode(const QObject* object) const;
244
    QByteArray branchBuffer(const QList<bool>& branches, bool isNodeLine, bool isLastChild) const;
245
    void dumpRecursive(const Node& node, QList<bool> branches, bool isLastChild);
246
    void dumpNode(const Node& node, const QList<bool>& branches, bool isLastChild);
247
248
private:
249
    unsigned m_indent;
250
251
    QScopedPointer<Node> m_root;
252
253
    // Hash table used to associate internal nodes with QObjects
254
    typedef QHash<const QObject*, Node*> Hash;
255
    Hash m_hash;
256
};
257
258
static const unsigned DefaultIndent = 2;
259
260
QVisitorPrivate::QVisitorPrivate()
261
    :   m_indent(DefaultIndent)
262
{
263
264
}
265
266
QVisitorPrivate::~QVisitorPrivate()
267
{
268
269
}
270
271
void QVisitorPrivate::setIndent(unsigned indent)
272
{
273
    m_indent = indent;
274
}
275
276
// Builds up a mirror of the object tree, rooted in m_root, with each node
277
// storing annotations generated by
278
void QVisitorPrivate::visitNode(const QObject& object)
279
{
280
    QObject* const objectParent = object.parent();
281
    Node* const nodeParent = objectParent ? findNode(objectParent) : 0;
282
283
    // Create a new node and store in scoped pointer for exception safety
284
    Node* node = new Node;
285
    QScopedPointer<Node> nodePtr(node);
286
287
    // Associate node with QObject
288
    m_hash.insert(&object, node);
289
290
    // Insert node into internal tree
291
    if (nodeParent)
292
    {
293
        nodeParent->m_children.append(nodePtr.take());
294
    }
295
    else
296
    {
297
        Q_ASSERT(m_root.isNull());
298
        m_root.reset(nodePtr.take());
299
    }
300
301
    // Generate and store annotations
302
    QAnnotator* annotator;
303
    foreach(annotator, m_annotators)
304
        node->m_annotation.append( annotator->annotation(object) );
305
}
306
307
void QVisitorPrivate::visitComplete()
308
{
309
    QList<bool> branches;
310
    static const bool isLastChild = true;
311
    dumpRecursive(*m_root, branches, isLastChild);
312
    m_root.reset(0);
313
}
314
315
QVisitorPrivate::Node* QVisitorPrivate::findNode(const QObject* object) const
316
{
317
    Hash::const_iterator i = m_hash.find(object);
318
    return (m_hash.end() == i) ? 0 : *i;
319
}
320
321
QByteArray QVisitorPrivate::branchBuffer
322
    (const QList<bool>& branches, bool isNodeLine, bool isLastChild) const
323
{
324
    const int depth = branches.count();
325
326
    const QByteArray indent(m_indent, ' ');
327
    const QByteArray horiz(m_indent, '-');
328
329
    QByteArray buffer;
330
    QTextStream stream(&buffer);
331
332
    for (int i=0; i<depth-1; ++i) {
333
        if (branches[i])
334
            stream << '|';
335
        else
336
            stream << ' ';
337
        stream << indent;
338
    }
339
340
    if (depth) {
341
        if (isNodeLine)
342
            stream << '+' << horiz;
343
        else {
344
            if (!isLastChild)
345
                stream << '|';
346
            else
347
                stream << ' ';
348
            stream << indent;
349
        }
350
    }
351
352
    stream.flush();
353
    buffer.push_front(m_prefix);
354
355
    return buffer;
356
}
357
358
void QVisitorPrivate::dumpRecursive
359
    (const Node& node, QList<bool> branches, bool isLastChild)
360
{
361
    dumpNode(node, branches, isLastChild);
362
363
    // Recurse down tree
364
    const Node::child_iterator begin = node.m_children.begin();
365
    const Node::child_iterator end = node.m_children.end();
366
    for (Node::child_iterator i = begin; end != i; ++i) {
367
368
        isLastChild = (end == i + 1);
369
370
        if (begin == i)
371
            branches.push_back(!isLastChild);
372
        else
373
            branches.back() = !isLastChild;
374
375
        static const bool isNodeLine = false;
376
        const QByteArray buffer = branchBuffer(branches, isNodeLine, false);
377
        qDebug() << buffer.constData();
378
379
        dumpRecursive(**i, branches, isLastChild);
380
    }
381
}
382
383
void QVisitorPrivate::dumpNode
384
    (const Node& node, const QList<bool>& branches, bool isLastChild)
385
{
386
    const QList<QByteArray>::const_iterator
387
        begin = node.m_annotation.begin(), end = node.m_annotation.end();
388
389
    if (begin == end) {
390
        // No annotations - just dump the object pointer
391
        const bool isNodeLine = true;
392
        QByteArray buffer = branchBuffer(branches, isNodeLine, isLastChild);
393
        qDebug() << 0;
394
    }
395
    else {
396
        // Dump annotations
397
        for (QList<QByteArray>::const_iterator i = begin; end != i; ++i) {
398
            const bool isNodeLine = (begin == i);
399
            QByteArray buffer = branchBuffer(branches, isNodeLine, isLastChild);
400
            buffer.append(*i);
401
            qDebug() << buffer.constData();
402
        }
403
    }
404
}
405
406
407
// QVisitorPrivate::Node
408
409
QVisitorPrivate::Node::Node()
410
{
411
412
}
413
414
QVisitorPrivate::Node::~Node()
415
{
416
    Node* child;
417
    foreach(child, m_children)
418
        delete child;
419
}
420
421
422
// QVisitor
423
424
QVisitor::QVisitor()
425
    : d_ptr(new QVisitorPrivate)
426
{
427
428
}
429
430
QVisitor::~QVisitor()
431
{
432
433
}
434
435
void QVisitor::setPrefix(const QString& prefix)
436
{
437
    d_func()->setPrefix(prefix);
438
}
439
440
void QVisitor::setIndent(unsigned indent)
441
{
442
    d_func()->setIndent(indent);
443
}
444
445
void QVisitor::addAnnotator(QAnnotator* annotator)
446
{
447
    d_func()->addAnnotator(annotator);
448
}
449
450
void QVisitor::visitPrepare()
451
{
452
    // Do nothing
453
}
454
455
void QVisitor::visitNode(const QObject& object)
456
{
457
    d_func()->visitNode(object);
458
}
459
460
void QVisitor::visitComplete()
461
{
462
    d_func()->visitComplete();
463
}
464
465
466
//-----------------------------------------------------------------------------
467
// Utility functions
468
//-----------------------------------------------------------------------------
469
470
void addDefaultAnnotators_sys(QDumper& visitor);
471
void addDefaultAnnotators_sys(QVisitor& visitor);
472
473
void addDefaultAnnotators(QDumper& dumper)
474
{
475
    dumper.addAnnotator(new QAnnotatorBasic);
476
    dumper.addAnnotator(new QAnnotatorWidget);
477
478
    // Add platform-specific annotators
479
    addDefaultAnnotators_sys(dumper);
480
}
481
482
void addDefaultAnnotators(QVisitor& visitor)
483
{
484
    visitor.addAnnotator(new QAnnotatorBasic);
485
    visitor.addAnnotator(new QAnnotatorWidget);
486
487
    // Add platform-specific annotators
488
    addDefaultAnnotators_sys(visitor);
489
}
490
491
void dumpTreeFromRoot(const QObject& root, QVisitor& visitor)
492
{
493
    // Set up iteration range
494
    ObjectTree::DepthFirstConstIterator begin(root), end;
495
496
    // Invoke generic visitor algorithm
497
    ObjectTree::visit(begin, end, visitor);
498
}
499
500
void dumpTreeFromLeaf(const QObject& leaf, QVisitor& visitor)
501
{
502
    // Walk up to root
503
    const QObject* root = &leaf;
504
    while(root->parent())
505
    {
506
        root = root->parent();
507
    }
508
509
    dumpTreeFromRoot(*root, visitor);
510
}
511
512
void dumpAncestors(const QObject& leaf, QVisitor& visitor)
513
{
514
    // Set up iteration range
515
    ObjectTree::AncestorConstIterator begin(leaf), end;
516
517
    // Invoke generic visitor algorithm
518
    ObjectTree::visit(begin, end, visitor);
519
}
520
521
522
} // namespace ObjectDump
523
524
QT_END_NAMESPACE