|
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; // TODO |
|
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 |
|
525 |