Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /**************************************************************************** |
---|---|
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the tools applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
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 Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | /* |
43 | tree.cpp |
44 | */ |
45 | |
46 | #include <QDomDocument> |
47 | |
48 | #include "atom.h" |
49 | #include "doc.h" |
50 | #include "htmlgenerator.h" |
51 | #include "location.h" |
52 | #include "node.h" |
53 | #include "text.h" |
54 | #include "tree.h" |
55 | |
56 | #include <limits.h> |
57 | |
58 | QT_BEGIN_NAMESPACE |
59 | |
60 | struct InheritanceBound |
61 | { |
62 | Node::Access access; |
63 | QStringList basePath; |
64 | QString dataTypeWithTemplateArgs; |
65 | InnerNode *parent; |
66 | |
67 | InheritanceBound() |
68 | : access(Node::Public) { } |
69 | InheritanceBound(Node::Access access0, |
70 | const QStringList& basePath0, |
71 | const QString &dataTypeWithTemplateArgs0, |
72 | InnerNode *parent) |
73 | : access(access0), basePath(basePath0), |
74 | dataTypeWithTemplateArgs(dataTypeWithTemplateArgs0), |
75 | parent(parent) { } |
76 | }; |
77 | |
78 | struct Target |
79 | { |
80 | Node *node; |
81 | Atom *atom; |
82 | int priority; |
83 | }; |
84 | |
85 | typedef QMap<PropertyNode::FunctionRole, QString> RoleMap; |
86 | typedef QMap<PropertyNode *, RoleMap> PropertyMap; |
87 | typedef QMultiMap<QString, Node *> GroupMap; |
88 | typedef QMultiHash<QString, FakeNode *> FakeNodeHash; |
89 | typedef QMultiHash<QString, Target> TargetHash; |
90 | |
91 | class TreePrivate |
92 | { |
93 | public: |
94 | QMap<ClassNode *, QList<InheritanceBound> > unresolvedInheritanceMap; |
95 | PropertyMap unresolvedPropertyMap; |
96 | GroupMap groupMap; |
97 | QMultiMap<QString, QString> publicGroupMap; |
98 | FakeNodeHash fakeNodesByTitle; |
99 | TargetHash targetHash; |
100 | QList<QPair<ClassNode*,QString> > basesList; |
101 | QList<QPair<FunctionNode*,QString> > relatedList; |
102 | }; |
103 | |
104 | /*! |
105 | \class Tree |
106 | */ |
107 | |
108 | /*! |
109 | The default constructor is the only constructor. |
110 | */ |
111 | Tree::Tree() |
112 | : roo(0, "") |
113 | { |
114 | priv = new TreePrivate; |
115 | } |
116 | |
117 | /*! |
118 | The destructor deletes the internal, private tree. |
119 | */ |
120 | Tree::~Tree() |
121 | { |
122 | delete priv; |
123 | } |
124 | |
125 | /*! |
126 | */ |
127 | Node *Tree::findNode(const QStringList &path, Node *relative, int findFlags, const Node* self) |
128 | { |
129 | return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path, |
130 | relative, |
131 | findFlags, |
132 | self)); |
133 | } |
134 | |
135 | /*! |
136 | */ |
137 | const Node* Tree::findNode(const QStringList &path, |
138 | const Node* start, |
139 | int findFlags, |
140 | const Node* self) const |
141 | { |
142 | const Node* current = start; |
143 | if (!current) |
144 | current = root(); |
145 | |
146 | do { |
147 | const Node *node = current; |
148 | int i; |
149 | |
150 | for (i = 0; i < path.size(); ++i) { |
151 | if (node == 0 || !node->isInnerNode()) |
152 | break; |
153 | |
154 | const Node *next = |
155 | static_cast<const InnerNode*>(node)->findNode(path.at(i)); |
156 | |
157 | if (!next && (findFlags & SearchEnumValues) && i == path.size()-1) |
158 | next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i)); |
159 | |
160 | if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { |
161 | NodeList baseClasses = allBaseClasses(static_cast<const ClassNode *>(node)); |
162 | foreach (const Node *baseClass, baseClasses) { |
163 | next = static_cast<const InnerNode *>(baseClass)->findNode(path.at(i)); |
164 | if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1) |
165 | next = static_cast<const InnerNode *>(baseClass) |
166 | ->findEnumNodeForValue(path.at(i)); |
167 | if (next) |
168 | break; |
169 | } |
170 | } |
171 | node = next; |
172 | } |
173 | if (node && i == path.size() |
174 | && (!(findFlags & NonFunction) || node->type() != Node::Function |
175 | || ((FunctionNode *)node)->metaness() == FunctionNode::MacroWithoutParams)) { |
176 | if ((node != self) && (node->subType() != Node::QmlPropertyGroup)) { |
177 | return node; |
178 | } |
179 | } |
180 | current = current->parent(); |
181 | } while (current); |
182 | |
183 | return 0; |
184 | } |
185 | |
186 | /*! |
187 | Find the node with the specified \a path name of the |
188 | specified \a type. |
189 | */ |
190 | Node *Tree::findNode(const QStringList &path, |
191 | Node::Type type, |
192 | Node *relative, |
193 | int findFlags) |
194 | { |
195 | return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path, |
196 | type, |
197 | relative, |
198 | findFlags)); |
199 | } |
200 | |
201 | /*! |
202 | Find the node with the specified \a path name of the |
203 | specified \a type. |
204 | */ |
205 | const Node *Tree::findNode(const QStringList &path, |
206 | Node::Type type, |
207 | const Node *relative, |
208 | int findFlags) const |
209 | { |
210 | const Node *node = findNode(path, relative, findFlags); |
211 | if (node != 0 && node->type() == type) |
212 | return node; |
213 | return 0; |
214 | } |
215 | |
216 | /*! |
217 | */ |
218 | FunctionNode *Tree::findFunctionNode(const QStringList& path, |
219 | Node *relative, |
220 | int findFlags) |
221 | { |
222 | return const_cast<FunctionNode *>( |
223 | const_cast<const Tree *>(this)->findFunctionNode(path, |
224 | relative, |
225 | findFlags)); |
226 | } |
227 | |
228 | /*! |
229 | */ |
230 | const FunctionNode *Tree::findFunctionNode(const QStringList &path, |
231 | const Node *relative, |
232 | int findFlags) const |
233 | { |
234 | if (!relative) |
235 | relative = root(); |
236 | |
237 | do { |
238 | const Node *node = relative; |
239 | int i; |
240 | |
241 | for (i = 0; i < path.size(); ++i) { |
242 | if (node == 0 || !node->isInnerNode()) |
243 | break; |
244 | |
245 | const Node *next; |
246 | if (i == path.size() - 1) |
247 | next = ((InnerNode *) node)->findFunctionNode(path.at(i)); |
248 | else |
249 | next = ((InnerNode *) node)->findNode(path.at(i)); |
250 | |
251 | if (!next && node->type() == Node::Class && |
252 | (findFlags & SearchBaseClasses)) { |
253 | NodeList baseClasses = allBaseClasses(static_cast<const ClassNode *>(node)); |
254 | foreach (const Node *baseClass, baseClasses) { |
255 | if (i == path.size() - 1) |
256 | next = static_cast<const InnerNode *>(baseClass)->findFunctionNode(path.at(i)); |
257 | else |
258 | next = static_cast<const InnerNode *>(baseClass)->findNode(path.at(i)); |
259 | |
260 | if (next) |
261 | break; |
262 | } |
263 | } |
264 | |
265 | node = next; |
266 | } |
267 | if (node && i == path.size() && node->isFunction()) { |
268 | // CppCodeParser::processOtherMetaCommand ensures that reimplemented |
269 | // functions are private. |
270 | const FunctionNode *func = static_cast<const FunctionNode*>(node); |
271 | while (func->access() == Node::Private) { |
272 | const FunctionNode *from = func->reimplementedFrom(); |
273 | if (from != 0) { |
274 | if (from->access() != Node::Private) |
275 | return from; |
276 | else |
277 | func = from; |
278 | } |
279 | else |
280 | break; |
281 | } |
282 | return func; |
283 | } |
284 | relative = relative->parent(); |
285 | } while (relative); |
286 | |
287 | return 0; |
288 | } |
289 | |
290 | /*! |
291 | */ |
292 | FunctionNode *Tree::findFunctionNode(const QStringList &parentPath, |
293 | const FunctionNode *clone, |
294 | Node *relative, |
295 | int findFlags) |
296 | { |
297 | return const_cast<FunctionNode *>( |
298 | const_cast<const Tree *>(this)->findFunctionNode(parentPath, |
299 | clone, |
300 | relative, |
301 | findFlags)); |
302 | } |
303 | |
304 | /*! |
305 | */ |
306 | const FunctionNode *Tree::findFunctionNode(const QStringList &parentPath, |
307 | const FunctionNode *clone, |
308 | const Node *relative, |
309 | int findFlags) const |
310 | { |
311 | const Node *parent = findNode(parentPath, relative, findFlags); |
312 | if (parent == 0 || !parent->isInnerNode()) { |
313 | return 0; |
314 | } |
315 | else { |
316 | return ((InnerNode *)parent)->findFunctionNode(clone); |
317 | } |
318 | } |
319 | |
320 | static const int NumSuffixes = 3; |
321 | static const char * const suffixes[NumSuffixes] = { "", "s", "es"}; |
322 | |
323 | /*! |
324 | */ |
325 | const FakeNode *Tree::findFakeNodeByTitle(const QString &title) const |
326 | { |
327 | for (int pass = 0; pass < NumSuffixes; ++pass) { |
328 | FakeNodeHash::const_iterator i = |
329 | priv->fakeNodesByTitle.find(Doc::canonicalTitle(title + suffixes[pass])); |
330 | if (i != priv->fakeNodesByTitle.constEnd()) { |
331 | FakeNodeHash::const_iterator j = i; |
332 | ++j; |
333 | if (j != priv->fakeNodesByTitle.constEnd() && j.key() == i.key()) { |
334 | QList<Location> internalLocations; |
335 | while (j != priv->fakeNodesByTitle.constEnd()) { |
336 | if (j.key() == i.key() && j.value()->url().isEmpty()) |
337 | internalLocations.append(j.value()->doc().location()); |
338 | ++j; |
339 | } |
340 | if (internalLocations.size() > 0) { |
341 | i.value()->doc().location().warning( |
342 | tr("Page '%1' defined in more than one location:").arg(title)); |
343 | foreach (const Location &location, internalLocations) |
344 | location.warning(tr("(defined here)")); |
345 | } |
346 | } |
347 | return i.value(); |
348 | } |
349 | } |
350 | return 0; |
351 | } |
352 | |
353 | /*! |
354 | */ |
355 | const Node* |
356 | Tree::findUnambiguousTarget(const QString &target, Atom *&atom) const |
357 | { |
358 | Target bestTarget = {0, 0, INT_MAX}; |
359 | int numBestTargets = 0; |
360 | |
361 | for (int pass = 0; pass < NumSuffixes; ++pass) { |
362 | TargetHash::const_iterator i = |
363 | priv->targetHash.find(Doc::canonicalTitle(target + suffixes[pass])); |
364 | if (i != priv->targetHash.constEnd()) { |
365 | TargetHash::const_iterator j = i; |
366 | do { |
367 | const Target &candidate = j.value(); |
368 | if (candidate.priority < bestTarget.priority) { |
369 | bestTarget = candidate; |
370 | numBestTargets = 1; |
371 | } else if (candidate.priority == bestTarget.priority) { |
372 | ++numBestTargets; |
373 | } |
374 | ++j; |
375 | } while (j != priv->targetHash.constEnd() && j.key() == i.key()); |
376 | |
377 | if (numBestTargets == 1) { |
378 | atom = bestTarget.atom; |
379 | return bestTarget.node; |
380 | } |
381 | } |
382 | } |
383 | return 0; |
384 | } |
385 | |
386 | /*! |
387 | */ |
388 | Atom *Tree::findTarget(const QString &target, const Node *node) const |
389 | { |
390 | for (int pass = 0; pass < NumSuffixes; ++pass) { |
391 | QString key = Doc::canonicalTitle(target + suffixes[pass]); |
392 | TargetHash::const_iterator i = priv->targetHash.find(key); |
393 | |
394 | if (i != priv->targetHash.constEnd()) { |
395 | do { |
396 | if (i.value().node == node) |
397 | return i.value().atom; |
398 | ++i; |
399 | } while (i != priv->targetHash.constEnd() && i.key() == key); |
400 | } |
401 | } |
402 | return 0; |
403 | } |
404 | |
405 | /*! |
406 | */ |
407 | void Tree::addBaseClass(ClassNode *subclass, Node::Access access, |
408 | const QStringList &basePath, |
409 | const QString &dataTypeWithTemplateArgs, |
410 | InnerNode *parent) |
411 | { |
412 | priv->unresolvedInheritanceMap[subclass].append( |
413 | InheritanceBound(access, |
414 | basePath, |
415 | dataTypeWithTemplateArgs, |
416 | parent) |
417 | ); |
418 | } |
419 | |
420 | |
421 | /*! |
422 | */ |
423 | void Tree::addPropertyFunction(PropertyNode *property, |
424 | const QString &funcName, |
425 | PropertyNode::FunctionRole funcRole) |
426 | { |
427 | priv->unresolvedPropertyMap[property].insert(funcRole, funcName); |
428 | } |
429 | |
430 | /*! |
431 | This function adds the \a node to the \a group. The group |
432 | can be listed anywhere using the \e{annotated list} command. |
433 | */ |
434 | void Tree::addToGroup(Node *node, const QString &group) |
435 | { |
436 | priv->groupMap.insert(group, node); |
437 | } |
438 | |
439 | /*! |
440 | */ |
441 | QMultiMap<QString, Node *> Tree::groups() const |
442 | { |
443 | return priv->groupMap; |
444 | } |
445 | |
446 | /*! |
447 | */ |
448 | void Tree::addToPublicGroup(Node *node, const QString &group) |
449 | { |
450 | priv->publicGroupMap.insert(node->name(), group); |
451 | addToGroup(node, group); |
452 | } |
453 | |
454 | /*! |
455 | */ |
456 | QMultiMap<QString, QString> Tree::publicGroups() const |
457 | { |
458 | return priv->publicGroupMap; |
459 | } |
460 | |
461 | /*! |
462 | */ |
463 | void Tree::resolveInheritance(NamespaceNode *rootNode) |
464 | { |
465 | if (!rootNode) |
466 | rootNode = root(); |
467 | |
468 | for (int pass = 0; pass < 2; pass++) { |
469 | NodeList::ConstIterator c = rootNode->childNodes().begin(); |
470 | while (c != rootNode->childNodes().end()) { |
471 | if ((*c)->type() == Node::Class) { |
472 | resolveInheritance(pass, (ClassNode *) *c); |
473 | } |
474 | else if ((*c)->type() == Node::Namespace) { |
475 | NamespaceNode *ns = static_cast<NamespaceNode*>(*c); |
476 | resolveInheritance(ns); |
477 | } |
478 | ++c; |
479 | } |
480 | if (rootNode == root()) |
481 | priv->unresolvedInheritanceMap.clear(); |
482 | } |
483 | } |
484 | |
485 | /*! |
486 | */ |
487 | void Tree::resolveProperties() |
488 | { |
489 | PropertyMap::ConstIterator propEntry; |
490 | |
491 | propEntry = priv->unresolvedPropertyMap.begin(); |
492 | while (propEntry != priv->unresolvedPropertyMap.end()) { |
493 | PropertyNode *property = propEntry.key(); |
494 | InnerNode *parent = property->parent(); |
495 | QString getterName = (*propEntry)[PropertyNode::Getter]; |
496 | QString setterName = (*propEntry)[PropertyNode::Setter]; |
497 | QString resetterName = (*propEntry)[PropertyNode::Resetter]; |
498 | QString notifierName = (*propEntry)[PropertyNode::Notifier]; |
499 | |
500 | NodeList::ConstIterator c = parent->childNodes().begin(); |
501 | while (c != parent->childNodes().end()) { |
502 | if ((*c)->type() == Node::Function) { |
503 | FunctionNode *function = static_cast<FunctionNode *>(*c); |
504 | if (function->access() == property->access() && |
505 | (function->status() == property->status() || |
506 | function->doc().isEmpty())) { |
507 | if (function->name() == getterName) { |
508 | property->addFunction(function, PropertyNode::Getter); |
509 | } else if (function->name() == setterName) { |
510 | property->addFunction(function, PropertyNode::Setter); |
511 | } else if (function->name() == resetterName) { |
512 | property->addFunction(function, PropertyNode::Resetter); |
513 | } else if (function->name() == notifierName) { |
514 | property->addSignal(function, PropertyNode::Notifier); |
515 | } |
516 | } |
517 | } |
518 | ++c; |
519 | } |
520 | ++propEntry; |
521 | } |
522 | |
523 | propEntry = priv->unresolvedPropertyMap.begin(); |
524 | while (propEntry != priv->unresolvedPropertyMap.end()) { |
525 | PropertyNode *property = propEntry.key(); |
526 | // redo it to set the property functions |
527 | if (property->overriddenFrom()) |
528 | property->setOverriddenFrom(property->overriddenFrom()); |
529 | ++propEntry; |
530 | } |
531 | |
532 | priv->unresolvedPropertyMap.clear(); |
533 | } |
534 | |
535 | /*! |
536 | */ |
537 | void Tree::resolveInheritance(int pass, ClassNode *classe) |
538 | { |
539 | if (pass == 0) { |
540 | QList<InheritanceBound> bounds = priv->unresolvedInheritanceMap[classe]; |
541 | QList<InheritanceBound>::ConstIterator b = bounds.begin(); |
542 | while (b != bounds.end()) { |
543 | ClassNode *baseClass = (ClassNode*)findNode((*b).basePath, |
544 | Node::Class); |
545 | if (!baseClass && (*b).parent) { |
546 | baseClass = (ClassNode*)findNode((*b).basePath, |
547 | Node::Class, |
548 | (*b).parent); |
549 | } |
550 | if (baseClass) { |
551 | classe->addBaseClass((*b).access, |
552 | baseClass, |
553 | (*b).dataTypeWithTemplateArgs); |
554 | } |
555 | ++b; |
556 | } |
557 | } |
558 | else { |
559 | NodeList::ConstIterator c = classe->childNodes().begin(); |
560 | while (c != classe->childNodes().end()) { |
561 | if ((*c)->type() == Node::Function) { |
562 | FunctionNode *func = (FunctionNode *) *c; |
563 | FunctionNode *from = findVirtualFunctionInBaseClasses(classe, func); |
564 | if (from != 0) { |
565 | if (func->virtualness() == FunctionNode::NonVirtual) |
566 | func->setVirtualness(FunctionNode::ImpureVirtual); |
567 | func->setReimplementedFrom(from); |
568 | } |
569 | } |
570 | else if ((*c)->type() == Node::Property) { |
571 | fixPropertyUsingBaseClasses(classe, static_cast<PropertyNode *>(*c)); |
572 | } |
573 | ++c; |
574 | } |
575 | } |
576 | } |
577 | |
578 | /*! |
579 | */ |
580 | void Tree::resolveGroups() |
581 | { |
582 | GroupMap::const_iterator i; |
583 | QString prevGroup; |
584 | for (i = priv->groupMap.constBegin(); i != priv->groupMap.constEnd(); ++i) { |
585 | if (i.value()->access() == Node::Private) |
586 | continue; |
587 | |
588 | FakeNode *fake = |
589 | static_cast<FakeNode*>(findNode(QStringList(i.key()),Node::Fake)); |
590 | if (fake && fake->subType() == Node::Group) { |
591 | fake->addGroupMember(i.value()); |
592 | } |
593 | |
594 | prevGroup = i.key(); |
595 | } |
596 | |
597 | //priv->groupMap.clear(); |
598 | } |
599 | |
600 | /*! |
601 | */ |
602 | void Tree::resolveTargets() |
603 | { |
604 | // need recursion |
605 | |
606 | foreach (Node *child, roo.childNodes()) { |
607 | if (child->type() == Node::Fake) { |
608 | FakeNode *node = static_cast<FakeNode *>(child); |
609 | priv->fakeNodesByTitle.insert(Doc::canonicalTitle(node->title()), node); |
610 | } |
611 | |
612 | if (child->doc().hasTableOfContents()) { |
613 | const QList<Atom *> &toc = child->doc().tableOfContents(); |
614 | Target target; |
615 | target.node = child; |
616 | target.priority = 3; |
617 | |
618 | for (int i = 0; i < toc.size(); ++i) { |
619 | target.atom = toc.at(i); |
620 | QString title = Text::sectionHeading(target.atom).toString(); |
621 | if (!title.isEmpty()) |
622 | priv->targetHash.insert(Doc::canonicalTitle(title), target); |
623 | } |
624 | } |
625 | if (child->doc().hasKeywords()) { |
626 | const QList<Atom *> &keywords = child->doc().keywords(); |
627 | Target target; |
628 | target.node = child; |
629 | target.priority = 1; |
630 | |
631 | for (int i = 0; i < keywords.size(); ++i) { |
632 | target.atom = keywords.at(i); |
633 | priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target); |
634 | } |
635 | } |
636 | if (child->doc().hasTargets()) { |
637 | const QList<Atom *> &toc = child->doc().targets(); |
638 | Target target; |
639 | target.node = child; |
640 | target.priority = 2; |
641 | |
642 | for (int i = 0; i < toc.size(); ++i) { |
643 | target.atom = toc.at(i); |
644 | priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target); |
645 | } |
646 | } |
647 | } |
648 | } |
649 | |
650 | /*! |
651 | */ |
652 | void Tree::fixInheritance(NamespaceNode *rootNode) |
653 | { |
654 | if (!rootNode) |
655 | rootNode = root(); |
656 | |
657 | NodeList::ConstIterator c = rootNode->childNodes().begin(); |
658 | while (c != rootNode->childNodes().end()) { |
659 | if ((*c)->type() == Node::Class) |
660 | static_cast<ClassNode *>(*c)->fixBaseClasses(); |
661 | else if ((*c)->type() == Node::Namespace) { |
662 | NamespaceNode *ns = static_cast<NamespaceNode*>(*c); |
663 | fixInheritance(ns); |
664 | } |
665 | ++c; |
666 | } |
667 | } |
668 | |
669 | /*! |
670 | */ |
671 | FunctionNode *Tree::findVirtualFunctionInBaseClasses(ClassNode *classe, |
672 | FunctionNode *clone) |
673 | { |
674 | QList<RelatedClass>::ConstIterator r = classe->baseClasses().begin(); |
675 | while (r != classe->baseClasses().end()) { |
676 | FunctionNode *func; |
677 | if (((func = findVirtualFunctionInBaseClasses((*r).node, clone)) != 0 || |
678 | (func = (*r).node->findFunctionNode(clone)) != 0)) { |
679 | if (func->virtualness() != FunctionNode::NonVirtual) |
680 | return func; |
681 | } |
682 | ++r; |
683 | } |
684 | return 0; |
685 | } |
686 | |
687 | /*! |
688 | */ |
689 | void Tree::fixPropertyUsingBaseClasses(ClassNode *classe, |
690 | PropertyNode *property) |
691 | { |
692 | QList<RelatedClass>::const_iterator r = classe->baseClasses().begin(); |
693 | while (r != classe->baseClasses().end()) { |
694 | PropertyNode *baseProperty = |
695 | static_cast<PropertyNode *>(r->node->findNode(property->name(), |
696 | Node::Property)); |
697 | if (baseProperty) { |
698 | fixPropertyUsingBaseClasses(r->node, baseProperty); |
699 | property->setOverriddenFrom(baseProperty); |
700 | } |
701 | else { |
702 | fixPropertyUsingBaseClasses(r->node, property); |
703 | } |
704 | ++r; |
705 | } |
706 | } |
707 | |
708 | /*! |
709 | */ |
710 | NodeList Tree::allBaseClasses(const ClassNode *classe) const |
711 | { |
712 | NodeList result; |
713 | foreach (const RelatedClass &r, classe->baseClasses()) { |
714 | result += r.node; |
715 | result += allBaseClasses(r.node); |
716 | } |
717 | return result; |
718 | } |
719 | |
720 | /*! |
721 | */ |
722 | void Tree::readIndexes(const QStringList &indexFiles) |
723 | { |
724 | foreach (const QString &indexFile, indexFiles) |
725 | readIndexFile(indexFile); |
726 | } |
727 | |
728 | /*! |
729 | Read the QDomDocument at \a path and get the index from it. |
730 | */ |
731 | void Tree::readIndexFile(const QString &path) |
732 | { |
733 | QFile file(path); |
734 | if (file.open(QFile::ReadOnly)) { |
735 | QDomDocument document; |
736 | document.setContent(&file); |
737 | file.close(); |
738 | |
739 | QDomElement indexElement = document.documentElement(); |
740 | QString indexUrl = indexElement.attribute("url", ""); |
741 | priv->basesList.clear(); |
742 | priv->relatedList.clear(); |
743 | |
744 | // Scan all elements in the XML file, constructing a map that contains |
745 | // base classes for each class found. |
746 | |
747 | QDomElement child = indexElement.firstChildElement(); |
748 | while (!child.isNull()) { |
749 | readIndexSection(child, root(), indexUrl); |
750 | child = child.nextSiblingElement(); |
751 | } |
752 | |
753 | // Now that all the base classes have been found for this index, |
754 | // arrange them into an inheritance hierarchy. |
755 | |
756 | resolveIndex(); |
757 | } |
758 | } |
759 | |
760 | /*! |
761 | */ |
762 | void Tree::readIndexSection(const QDomElement &element, |
763 | InnerNode *parent, |
764 | const QString &indexUrl) |
765 | { |
766 | QString name = element.attribute("name"); |
767 | QString href = element.attribute("href"); |
768 | |
769 | Node *section; |
770 | Location location; |
771 | |
772 | if (element.nodeName() == "namespace") { |
773 | section = new NamespaceNode(parent, name); |
774 | |
775 | if (!indexUrl.isEmpty()) |
776 | location = Location(indexUrl + "/"+ name.toLower() + ".html"); |
777 | else if (!indexUrl.isNull()) |
778 | location = Location(name.toLower() + ".html"); |
779 | |
780 | } |
781 | else if (element.nodeName() == "class") { |
782 | section = new ClassNode(parent, name); |
783 | priv->basesList.append(QPair<ClassNode*,QString>( |
784 | static_cast<ClassNode*>(section), element.attribute("bases"))); |
785 | |
786 | if (!indexUrl.isEmpty()) |
787 | location = Location(indexUrl + "/"+ name.toLower() + ".html"); |
788 | else if (!indexUrl.isNull()) |
789 | location = Location(name.toLower() + ".html"); |
790 | |
791 | } |
792 | else if (element.nodeName() == "page") { |
793 | Node::SubType subtype; |
794 | if (element.attribute("subtype") == "example") |
795 | subtype = Node::Example; |
796 | else if (element.attribute("subtype") == "header") |
797 | subtype = Node::HeaderFile; |
798 | else if (element.attribute("subtype") == "file") |
799 | subtype = Node::File; |
800 | else if (element.attribute("subtype") == "group") |
801 | subtype = Node::Group; |
802 | else if (element.attribute("subtype") == "module") |
803 | subtype = Node::Module; |
804 | else if (element.attribute("subtype") == "page") |
805 | subtype = Node::Page; |
806 | else if (element.attribute("subtype") == "externalpage") |
807 | subtype = Node::ExternalPage; |
808 | else if (element.attribute("subtype") == "qmlclass") |
809 | subtype = Node::QmlClass; |
810 | else if (element.attribute("subtype") == "qmlpropertygroup") |
811 | subtype = Node::QmlPropertyGroup; |
812 | else if (element.attribute("subtype") == "qmlbasictype") |
813 | subtype = Node::QmlBasicType; |
814 | else |
815 | return; |
816 | |
817 | FakeNode *fakeNode = new FakeNode(parent, name, subtype); |
818 | fakeNode->setTitle(element.attribute("title")); |
819 | |
820 | if (element.hasAttribute("location")) |
821 | name = element.attribute("location", ""); |
822 | |
823 | if (!indexUrl.isEmpty()) |
824 | location = Location(indexUrl + "/"+ name); |
825 | else if (!indexUrl.isNull()) |
826 | location = Location(name); |
827 | |
828 | section = fakeNode; |
829 | |
830 | } |
831 | else if (element.nodeName() == "enum") { |
832 | EnumNode *enumNode = new EnumNode(parent, name); |
833 | |
834 | if (!indexUrl.isEmpty()) |
835 | location = |
836 | Location(indexUrl + "/"+ parent->name().toLower() + ".html"); |
837 | else if (!indexUrl.isNull()) |
838 | location = Location(parent->name().toLower() + ".html"); |
839 | |
840 | QDomElement child = element.firstChildElement("value"); |
841 | while (!child.isNull()) { |
842 | EnumItem item(child.attribute("name"), child.attribute( "value")); |
843 | enumNode->addItem(item); |
844 | child = child.nextSiblingElement("value"); |
845 | } |
846 | |
847 | section = enumNode; |
848 | |
849 | } else if (element.nodeName() == "typedef") { |
850 | section = new TypedefNode(parent, name); |
851 | |
852 | if (!indexUrl.isEmpty()) |
853 | location = |
854 | Location(indexUrl + "/"+ parent->name().toLower() + ".html"); |
855 | else if (!indexUrl.isNull()) |
856 | location = Location(parent->name().toLower() + ".html"); |
857 | |
858 | } |
859 | else if (element.nodeName() == "property") { |
860 | section = new PropertyNode(parent, name); |
861 | |
862 | if (!indexUrl.isEmpty()) |
863 | location = |
864 | Location(indexUrl + "/"+ parent->name().toLower() + ".html"); |
865 | else if (!indexUrl.isNull()) |
866 | location = Location(parent->name().toLower() + ".html"); |
867 | |
868 | } else if (element.nodeName() == "function") { |
869 | FunctionNode::Virtualness virt; |
870 | if (element.attribute("virtual") == "non") |
871 | virt = FunctionNode::NonVirtual; |
872 | else if (element.attribute("virtual") == "impure") |
873 | virt = FunctionNode::ImpureVirtual; |
874 | else if (element.attribute("virtual") == "pure") |
875 | virt = FunctionNode::PureVirtual; |
876 | else |
877 | return; |
878 | |
879 | FunctionNode::Metaness meta; |
880 | if (element.attribute("meta") == "plain") |
881 | meta = FunctionNode::Plain; |
882 | else if (element.attribute("meta") == "signal") |
883 | meta = FunctionNode::Signal; |
884 | else if (element.attribute("meta") == "slot") |
885 | meta = FunctionNode::Slot; |
886 | else if (element.attribute("meta") == "constructor") |
887 | meta = FunctionNode::Ctor; |
888 | else if (element.attribute("meta") == "destructor") |
889 | meta = FunctionNode::Dtor; |
890 | else if (element.attribute("meta") == "macro") |
891 | meta = FunctionNode::MacroWithParams; |
892 | else if (element.attribute("meta") == "macrowithparams") |
893 | meta = FunctionNode::MacroWithParams; |
894 | else if (element.attribute("meta") == "macrowithoutparams") |
895 | meta = FunctionNode::MacroWithoutParams; |
896 | else |
897 | return; |
898 | |
899 | FunctionNode *functionNode = new FunctionNode(parent, name); |
900 | functionNode->setReturnType(element.attribute("return")); |
901 | functionNode->setVirtualness(virt); |
902 | functionNode->setMetaness(meta); |
903 | functionNode->setConst(element.attribute("const") == "true"); |
904 | functionNode->setStatic(element.attribute("static") == "true"); |
905 | functionNode->setOverload(element.attribute("overload") == "true"); |
906 | |
907 | if (element.hasAttribute("relates") |
908 | && element.attribute("relates") != parent->name()) { |
909 | priv->relatedList.append( |
910 | QPair<FunctionNode*,QString>(functionNode, |
911 | element.attribute("relates"))); |
912 | } |
913 | |
914 | QDomElement child = element.firstChildElement("parameter"); |
915 | while (!child.isNull()) { |
916 | // Do not use the default value for the parameter; it is not |
917 | // required, and has been known to cause problems. |
918 | Parameter parameter(child.attribute("left"), |
919 | child.attribute("right"), |
920 | child.attribute("name"), |
921 | ""); // child.attribute("default") |
922 | functionNode->addParameter(parameter); |
923 | child = child.nextSiblingElement("parameter"); |
924 | } |
925 | |
926 | section = functionNode; |
927 | |
928 | if (!indexUrl.isEmpty()) |
929 | location = |
930 | Location(indexUrl + "/"+ parent->name().toLower() + ".html"); |
931 | else if (!indexUrl.isNull()) |
932 | location = Location(parent->name().toLower() + ".html"); |
933 | |
934 | } |
935 | else if (element.nodeName() == "variable") { |
936 | section = new VariableNode(parent, name); |
937 | |
938 | if (!indexUrl.isEmpty()) |
939 | location = Location(indexUrl + "/"+ parent->name().toLower() + ".html"); |
940 | else if (!indexUrl.isNull()) |
941 | location = Location(parent->name().toLower() + ".html"); |
942 | |
943 | } |
944 | else if (element.nodeName() == "keyword") { |
945 | Target target; |
946 | target.node = parent; |
947 | target.priority = 1; |
948 | target.atom = new Atom(Atom::Target, name); |
949 | priv->targetHash.insert(name, target); |
950 | return; |
951 | |
952 | } |
953 | else if (element.nodeName() == "target") { |
954 | Target target; |
955 | target.node = parent; |
956 | target.priority = 2; |
957 | target.atom = new Atom(Atom::Target, name); |
958 | priv->targetHash.insert(name, target); |
959 | return; |
960 | |
961 | } |
962 | else if (element.nodeName() == "contents") { |
963 | Target target; |
964 | target.node = parent; |
965 | target.priority = 3; |
966 | target.atom = new Atom(Atom::Target, name); |
967 | priv->targetHash.insert(name, target); |
968 | return; |
969 | |
970 | } |
971 | else |
972 | return; |
973 | |
974 | QString access = element.attribute("access"); |
975 | if (access == "public") |
976 | section->setAccess(Node::Public); |
977 | else if (access == "protected") |
978 | section->setAccess(Node::Protected); |
979 | else if (access == "private") |
980 | section->setAccess(Node::Private); |
981 | else |
982 | section->setAccess(Node::Public); |
983 | |
984 | if (element.nodeName() != "page") { |
985 | QString threadSafety = element.attribute("threadsafety"); |
986 | if (threadSafety == "non-reentrant") |
987 | section->setThreadSafeness(Node::NonReentrant); |
988 | else if (threadSafety == "reentrant") |
989 | section->setThreadSafeness(Node::Reentrant); |
990 | else if (threadSafety == "thread safe") |
991 | section->setThreadSafeness(Node::ThreadSafe); |
992 | else |
993 | section->setThreadSafeness(Node::UnspecifiedSafeness); |
994 | } |
995 | else |
996 | section->setThreadSafeness(Node::UnspecifiedSafeness); |
997 | |
998 | QString status = element.attribute("status"); |
999 | if (status == "compat") |
1000 | section->setStatus(Node::Compat); |
1001 | else if (status == "obsolete") |
1002 | section->setStatus(Node::Obsolete); |
1003 | else if (status == "deprecated") |
1004 | section->setStatus(Node::Deprecated); |
1005 | else if (status == "preliminary") |
1006 | section->setStatus(Node::Preliminary); |
1007 | else if (status == "commendable") |
1008 | section->setStatus(Node::Commendable); |
1009 | else if (status == "internal") |
1010 | section->setStatus(Node::Internal); |
1011 | else if (status == "main") |
1012 | section->setStatus(Node::Main); |
1013 | else |
1014 | section->setStatus(Node::Commendable); |
1015 | |
1016 | section->setModuleName(element.attribute("module")); |
1017 | if (!indexUrl.isEmpty()) { |
1018 | if (indexUrl.startsWith(".")) |
1019 | section->setUrl(href); |
1020 | else |
1021 | section->setUrl(indexUrl + "/"+ href); |
1022 | } |
1023 | |
1024 | // Create some content for the node. |
1025 | QSet<QString> emptySet; |
1026 | |
1027 | Doc doc(location, location, " ", emptySet); // placeholder |
1028 | section->setDoc(doc); |
1029 | |
1030 | if (section->isInnerNode()) { |
1031 | InnerNode *inner = static_cast<InnerNode*>(section); |
1032 | if (inner) { |
1033 | QDomElement child = element.firstChildElement(); |
1034 | |
1035 | while (!child.isNull()) { |
1036 | if (element.nodeName() == "class") |
1037 | readIndexSection(child, inner, indexUrl); |
1038 | else if (element.nodeName() == "page") |
1039 | readIndexSection(child, inner, indexUrl); |
1040 | else if (element.nodeName() == "namespace"&& !name.isEmpty()) |
1041 | // The root node in the index is a namespace with an empty name. |
1042 | readIndexSection(child, inner, indexUrl); |
1043 | else |
1044 | readIndexSection(child, parent, indexUrl); |
1045 | |
1046 | child = child.nextSiblingElement(); |
1047 | } |
1048 | } |
1049 | } |
1050 | } |
1051 | |
1052 | /*! |
1053 | */ |
1054 | QString Tree::readIndexText(const QDomElement &element) |
1055 | { |
1056 | QString text; |
1057 | QDomNode child = element.firstChild(); |
1058 | while (!child.isNull()) { |
1059 | if (child.isText()) |
1060 | text += child.toText().nodeValue(); |
1061 | child = child.nextSibling(); |
1062 | } |
1063 | return text; |
1064 | } |
1065 | |
1066 | /*! |
1067 | */ |
1068 | void Tree::resolveIndex() |
1069 | { |
1070 | QPair<ClassNode*,QString> pair; |
1071 | |
1072 | foreach (pair, priv->basesList) { |
1073 | foreach (const QString &base, pair.second.split(",")) { |
1074 | Node *baseClass = root()->findNode(base, Node::Class); |
1075 | if (baseClass) { |
1076 | pair.first->addBaseClass(Node::Public, |
1077 | static_cast<ClassNode*>(baseClass)); |
1078 | } |
1079 | } |
1080 | } |
1081 | |
1082 | QPair<FunctionNode*,QString> relatedPair; |
1083 | |
1084 | foreach (relatedPair, priv->relatedList) { |
1085 | Node *classNode = root()->findNode(relatedPair.second, Node::Class); |
1086 | if (classNode) |
1087 | relatedPair.first->setRelates(static_cast<ClassNode*>(classNode)); |
1088 | } |
1089 | } |
1090 | |
1091 | /*! |
1092 | Generate the index section with the given \a writer for the \a node |
1093 | specified, returning true if an element was written; otherwise returns |
1094 | false. |
1095 | */ |
1096 | bool Tree::generateIndexSection(QXmlStreamWriter &writer, |
1097 | const Node *node, |
1098 | bool generateInternalNodes) const |
1099 | { |
1100 | if (!node->url().isEmpty()) |
1101 | return false; |
1102 | |
1103 | QString nodeName; |
1104 | switch (node->type()) { |
1105 | case Node::Namespace: |
1106 | nodeName = "namespace"; |
1107 | break; |
1108 | case Node::Class: |
1109 | nodeName = "class"; |
1110 | break; |
1111 | case Node::Fake: |
1112 | nodeName = "page"; |
1113 | break; |
1114 | case Node::Enum: |
1115 | nodeName = "enum"; |
1116 | break; |
1117 | case Node::Typedef: |
1118 | nodeName = "typedef"; |
1119 | break; |
1120 | case Node::Property: |
1121 | nodeName = "property"; |
1122 | break; |
1123 | case Node::Function: |
1124 | nodeName = "function"; |
1125 | break; |
1126 | case Node::Variable: |
1127 | nodeName = "variable"; |
1128 | break; |
1129 | case Node::Target: |
1130 | nodeName = "target"; |
1131 | break; |
1132 | case Node::QmlProperty: |
1133 | nodeName = "qmlproperty"; |
1134 | break; |
1135 | case Node::QmlSignal: |
1136 | nodeName = "qmlsignal"; |
1137 | break; |
1138 | case Node::QmlMethod: |
1139 | nodeName = "qmlmethod"; |
1140 | break; |
1141 | default: |
1142 | return false; |
1143 | } |
1144 | |
1145 | QString access; |
1146 | switch (node->access()) { |
1147 | case Node::Public: |
1148 | access = "public"; |
1149 | break; |
1150 | case Node::Protected: |
1151 | access = "protected"; |
1152 | break; |
1153 | case Node::Private: |
1154 | // Do not include private non-internal nodes in the index. |
1155 | // (Internal public and protected nodes are marked as private |
1156 | // by qdoc. We can check their internal status to determine |
1157 | // whether they were really private to begin with.) |
1158 | if (node->status() == Node::Internal && generateInternalNodes) |
1159 | access = "internal"; |
1160 | else |
1161 | return false; |
1162 | break; |
1163 | default: |
1164 | return false; |
1165 | } |
1166 | |
1167 | QString objName = node->name(); |
1168 | |
1169 | // Special case: only the root node should have an empty name. |
1170 | if (objName.isEmpty() && node != root()) |
1171 | return false; |
1172 | |
1173 | writer.writeStartElement(nodeName); |
1174 | |
1175 | QXmlStreamAttributes attributes; |
1176 | writer.writeAttribute("access", access); |
1177 | |
1178 | if (node->type() != Node::Fake) { |
1179 | QString threadSafety; |
1180 | switch (node->threadSafeness()) { |
1181 | case Node::NonReentrant: |
1182 | threadSafety = "non-reentrant"; |
1183 | break; |
1184 | case Node::Reentrant: |
1185 | threadSafety = "reentrant"; |
1186 | break; |
1187 | case Node::ThreadSafe: |
1188 | threadSafety = "thread safe"; |
1189 | break; |
1190 | case Node::UnspecifiedSafeness: |
1191 | default: |
1192 | threadSafety = "unspecified"; |
1193 | break; |
1194 | } |
1195 | writer.writeAttribute("threadsafety", threadSafety); |
1196 | } |
1197 | |
1198 | QString status; |
1199 | switch (node->status()) { |
1200 | case Node::Compat: |
1201 | status = "compat"; |
1202 | break; |
1203 | case Node::Obsolete: |
1204 | status = "obsolete"; |
1205 | break; |
1206 | case Node::Deprecated: |
1207 | status = "deprecated"; |
1208 | break; |
1209 | case Node::Preliminary: |
1210 | status = "preliminary"; |
1211 | break; |
1212 | case Node::Commendable: |
1213 | status = "commendable"; |
1214 | break; |
1215 | case Node::Internal: |
1216 | status = "internal"; |
1217 | break; |
1218 | case Node::Main: |
1219 | default: |
1220 | status = "main"; |
1221 | break; |
1222 | } |
1223 | writer.writeAttribute("status", status); |
1224 | |
1225 | writer.writeAttribute("name", objName); |
1226 | QString fullName = fullDocumentName(node); |
1227 | if (fullName != objName) |
1228 | writer.writeAttribute("fullname", fullName); |
1229 | writer.writeAttribute("href", HtmlGenerator::fullDocumentLocation(node)); |
1230 | if ((node->type() != Node::Fake) && (!node->isQmlNode())) |
1231 | writer.writeAttribute("location", node->location().fileName()); |
1232 | |
1233 | switch (node->type()) { |
1234 | |
1235 | case Node::Class: |
1236 | { |
1237 | // Classes contain information about their base classes. |
1238 | |
1239 | const ClassNode *classNode = static_cast<const ClassNode*>(node); |
1240 | QList<RelatedClass> bases = classNode->baseClasses(); |
1241 | QSet<QString> baseStrings; |
1242 | foreach (const RelatedClass &related, bases) { |
1243 | ClassNode *baseClassNode = related.node; |
1244 | baseStrings.insert(baseClassNode->name()); |
1245 | } |
1246 | writer.writeAttribute("bases", QStringList(baseStrings.toList()).join( ",")); |
1247 | writer.writeAttribute("module", node->moduleName()); |
1248 | } |
1249 | break; |
1250 | |
1251 | case Node::Namespace: |
1252 | writer.writeAttribute("module", node->moduleName()); |
1253 | break; |
1254 | |
1255 | case Node::Fake: |
1256 | { |
1257 | /* |
1258 | Fake nodes (such as manual pages) contain subtypes, |
1259 | titles and other attributes. |
1260 | */ |
1261 | |
1262 | const FakeNode *fakeNode = static_cast<const FakeNode*>(node); |
1263 | switch (fakeNode->subType()) { |
1264 | case Node::Example: |
1265 | writer.writeAttribute("subtype", "example"); |
1266 | break; |
1267 | case Node::HeaderFile: |
1268 | writer.writeAttribute("subtype", "header"); |
1269 | break; |
1270 | case Node::File: |
1271 | writer.writeAttribute("subtype", "file"); |
1272 | break; |
1273 | case Node::Group: |
1274 | writer.writeAttribute("subtype", "group"); |
1275 | break; |
1276 | case Node::Module: |
1277 | writer.writeAttribute("subtype", "module"); |
1278 | break; |
1279 | case Node::Page: |
1280 | writer.writeAttribute("subtype", "page"); |
1281 | break; |
1282 | case Node::ExternalPage: |
1283 | writer.writeAttribute("subtype", "externalpage"); |
1284 | break; |
1285 | case Node::QmlClass: |
1286 | writer.writeAttribute("subtype", "qmlclass"); |
1287 | break; |
1288 | case Node::QmlBasicType: |
1289 | writer.writeAttribute("subtype", "qmlbasictype"); |
1290 | break; |
1291 | default: |
1292 | break; |
1293 | } |
1294 | writer.writeAttribute("title", fakeNode->title()); |
1295 | writer.writeAttribute("fulltitle", fakeNode->fullTitle()); |
1296 | writer.writeAttribute("subtitle", fakeNode->subTitle()); |
1297 | writer.writeAttribute("location", fakeNode->doc().location().fileName()); |
1298 | } |
1299 | break; |
1300 | |
1301 | case Node::Function: |
1302 | { |
1303 | /* |
1304 | Function nodes contain information about the type of |
1305 | function being described. |
1306 | */ |
1307 | |
1308 | const FunctionNode *functionNode = |
1309 | static_cast<const FunctionNode*>(node); |
1310 | |
1311 | switch (functionNode->virtualness()) { |
1312 | case FunctionNode::NonVirtual: |
1313 | writer.writeAttribute("virtual", "non"); |
1314 | break; |
1315 | case FunctionNode::ImpureVirtual: |
1316 | writer.writeAttribute("virtual", "impure"); |
1317 | break; |
1318 | case FunctionNode::PureVirtual: |
1319 | writer.writeAttribute("virtual", "pure"); |
1320 | break; |
1321 | default: |
1322 | break; |
1323 | } |
1324 | switch (functionNode->metaness()) { |
1325 | case FunctionNode::Plain: |
1326 | writer.writeAttribute("meta", "plain"); |
1327 | break; |
1328 | case FunctionNode::Signal: |
1329 | writer.writeAttribute("meta", "signal"); |
1330 | break; |
1331 | case FunctionNode::Slot: |
1332 | writer.writeAttribute("meta", "slot"); |
1333 | break; |
1334 | case FunctionNode::Ctor: |
1335 | writer.writeAttribute("meta", "constructor"); |
1336 | break; |
1337 | case FunctionNode::Dtor: |
1338 | writer.writeAttribute("meta", "destructor"); |
1339 | break; |
1340 | case FunctionNode::MacroWithParams: |
1341 | writer.writeAttribute("meta", "macrowithparams"); |
1342 | break; |
1343 | case FunctionNode::MacroWithoutParams: |
1344 | writer.writeAttribute("meta", "macrowithoutparams"); |
1345 | break; |
1346 | default: |
1347 | break; |
1348 | } |
1349 | writer.writeAttribute("const", functionNode->isConst()? "true": "false"); |
1350 | writer.writeAttribute("static", functionNode->isStatic()? "true": "false"); |
1351 | writer.writeAttribute("overload", functionNode->isOverload()? "true": "false"); |
1352 | if (functionNode->isOverload()) |
1353 | writer.writeAttribute("overload-number", QString::number(functionNode->overloadNumber())); |
1354 | if (functionNode->relates()) |
1355 | writer.writeAttribute("relates", functionNode->relates()->name()); |
1356 | const PropertyNode *propertyNode = functionNode->associatedProperty(); |
1357 | if (propertyNode) |
1358 | writer.writeAttribute("associated-property", propertyNode->name()); |
1359 | writer.writeAttribute("type", functionNode->returnType()); |
1360 | } |
1361 | break; |
1362 | |
1363 | case Node::QmlProperty: |
1364 | { |
1365 | const QmlPropertyNode *qpn = static_cast<const QmlPropertyNode*>(node); |
1366 | writer.writeAttribute("type", qpn->dataType()); |
1367 | writer.writeAttribute("attached", qpn->isAttached() ? "true": "false"); |
1368 | writer.writeAttribute("writable", qpn->isWritable(this) ? "true": "false"); |
1369 | } |
1370 | break; |
1371 | case Node::Property: |
1372 | { |
1373 | const PropertyNode *propertyNode = static_cast<const PropertyNode*>(node); |
1374 | writer.writeAttribute("type", propertyNode->dataType()); |
1375 | foreach (const Node *fnNode, propertyNode->getters()) { |
1376 | if (fnNode) { |
1377 | const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode); |
1378 | writer.writeStartElement("getter"); |
1379 | writer.writeAttribute("name", functionNode->name()); |
1380 | writer.writeEndElement(); // getter |
1381 | } |
1382 | } |
1383 | foreach (const Node *fnNode, propertyNode->setters()) { |
1384 | if (fnNode) { |
1385 | const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode); |
1386 | writer.writeStartElement("setter"); |
1387 | writer.writeAttribute("name", functionNode->name()); |
1388 | writer.writeEndElement(); // getter |
1389 | } |
1390 | } |
1391 | foreach (const Node *fnNode, propertyNode->resetters()) { |
1392 | if (fnNode) { |
1393 | const FunctionNode *functionNode = static_cast<const FunctionNode*>(fnNode); |
1394 | writer.writeStartElement("resetter"); |
1395 | writer.writeAttribute("name", functionNode->name()); |
1396 | writer.writeEndElement(); // getter |
1397 | } |
1398 | } |
1399 | } |
1400 | break; |
1401 | |
1402 | case Node::Variable: |
1403 | { |
1404 | const VariableNode *variableNode = |
1405 | static_cast<const VariableNode*>(node); |
1406 | writer.writeAttribute("type", variableNode->dataType()); |
1407 | writer.writeAttribute("static", |
1408 | variableNode->isStatic() ? "true": "false"); |
1409 | } |
1410 | break; |
1411 | default: |
1412 | break; |
1413 | } |
1414 | |
1415 | // Inner nodes and function nodes contain child nodes of some sort, either |
1416 | // actual child nodes or function parameters. For these, we close the |
1417 | // opening tag, create child elements, then add a closing tag for the |
1418 | // element. Elements for all other nodes are closed in the opening tag. |
1419 | |
1420 | if (node->isInnerNode()) { |
1421 | |
1422 | const InnerNode *inner = static_cast<const InnerNode*>(node); |
1423 | |
1424 | // For internal pages, we canonicalize the target, keyword and content |
1425 | // item names so that they can be used by qdoc for other sets of |
1426 | // documentation. |
1427 | // The reason we do this here is that we don't want to ruin |
1428 | // externally composed indexes, containing non-qdoc-style target names |
1429 | // when reading in indexes. |
1430 | |
1431 | if (inner->doc().hasTargets()) { |
1432 | bool external = false; |
1433 | if (inner->type() == Node::Fake) { |
1434 | const FakeNode *fakeNode = static_cast<const FakeNode *>(inner); |
1435 | if (fakeNode->subType() == Node::ExternalPage) |
1436 | external = true; |
1437 | } |
1438 | |
1439 | foreach (const Atom *target, inner->doc().targets()) { |
1440 | QString targetName = target->string(); |
1441 | if (!external) |
1442 | targetName = Doc::canonicalTitle(targetName); |
1443 | |
1444 | writer.writeStartElement("target"); |
1445 | writer.writeAttribute("name", targetName); |
1446 | writer.writeEndElement(); // target |
1447 | } |
1448 | } |
1449 | if (inner->doc().hasKeywords()) { |
1450 | foreach (const Atom *keyword, inner->doc().keywords()) { |
1451 | writer.writeStartElement("keyword"); |
1452 | writer.writeAttribute("name", |
1453 | Doc::canonicalTitle(keyword->string())); |
1454 | writer.writeEndElement(); // keyword |
1455 | } |
1456 | } |
1457 | if (inner->doc().hasTableOfContents()) { |
1458 | for (int i = 0; i < inner->doc().tableOfContents().size(); ++i) { |
1459 | Atom *item = inner->doc().tableOfContents()[i]; |
1460 | int level = inner->doc().tableOfContentsLevels()[i]; |
1461 | |
1462 | QString title = Text::sectionHeading(item).toString(); |
1463 | writer.writeStartElement("contents"); |
1464 | writer.writeAttribute("name", Doc::canonicalTitle(title)); |
1465 | writer.writeAttribute("title", title); |
1466 | writer.writeAttribute("level", QString::number(level)); |
1467 | writer.writeEndElement(); // contents |
1468 | } |
1469 | } |
1470 | |
1471 | } |
1472 | else if (node->type() == Node::Function) { |
1473 | |
1474 | const FunctionNode *functionNode = static_cast<const FunctionNode*>(node); |
1475 | // Write a signature attribute for convenience. |
1476 | QStringList signatureList; |
1477 | QStringList resolvedParameters; |
1478 | |
1479 | foreach (const Parameter ¶meter, functionNode->parameters()) { |
1480 | QString leftType = parameter.leftType(); |
1481 | const Node *leftNode = |
1482 | const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"), |
1483 | Node::Typedef, 0, SearchBaseClasses|NonFunction); |
1484 | if (!leftNode) { |
1485 | leftNode = const_cast<Tree *>(this)->findNode( |
1486 | parameter.leftType().split("::"), Node::Typedef, |
1487 | node->parent(), SearchBaseClasses|NonFunction); |
1488 | } |
1489 | if (leftNode) { |
1490 | if (leftNode->type() == Node::Typedef) { |
1491 | const TypedefNode *typedefNode = |
1492 | static_cast<const TypedefNode *>(leftNode); |
1493 | if (typedefNode->associatedEnum()) { |
1494 | leftType = "QFlags<"+fullDocumentName(typedefNode->associatedEnum())+ ">"; |
1495 | } |
1496 | } |
1497 | else |
1498 | leftType = fullDocumentName(leftNode); |
1499 | } |
1500 | resolvedParameters.append(leftType); |
1501 | signatureList.append(leftType + " "+ parameter.name()); |
1502 | } |
1503 | |
1504 | QString signature = functionNode->name()+"("+signatureList.join( ", ")+ ")"; |
1505 | if (functionNode->isConst()) |
1506 | signature += " const"; |
1507 | writer.writeAttribute("signature", signature); |
1508 | |
1509 | for (int i = 0; i < functionNode->parameters().size(); ++i) { |
1510 | Parameter parameter = functionNode->parameters()[i]; |
1511 | writer.writeStartElement("parameter"); |
1512 | writer.writeAttribute("left", resolvedParameters[i]); |
1513 | writer.writeAttribute("right", parameter.rightType()); |
1514 | writer.writeAttribute("name", parameter.name()); |
1515 | writer.writeAttribute("default", parameter.defaultValue()); |
1516 | writer.writeEndElement(); // parameter |
1517 | } |
1518 | |
1519 | } |
1520 | else if (node->type() == Node::Enum) { |
1521 | |
1522 | const EnumNode *enumNode = static_cast<const EnumNode*>(node); |
1523 | if (enumNode->flagsType()) { |
1524 | writer.writeAttribute("typedef", |
1525 | fullDocumentName(enumNode->flagsType())); |
1526 | } |
1527 | foreach (const EnumItem &item, enumNode->items()) { |
1528 | writer.writeStartElement("value"); |
1529 | writer.writeAttribute("name", item.name()); |
1530 | writer.writeAttribute("value", item.value()); |
1531 | writer.writeEndElement(); // value |
1532 | } |
1533 | |
1534 | } |
1535 | else if (node->type() == Node::Typedef) { |
1536 | |
1537 | const TypedefNode *typedefNode = static_cast<const TypedefNode*>(node); |
1538 | if (typedefNode->associatedEnum()) { |
1539 | writer.writeAttribute("enum", |
1540 | fullDocumentName(typedefNode->associatedEnum())); |
1541 | } |
1542 | } |
1543 | |
1544 | return true; |
1545 | } |
1546 | |
1547 | |
1548 | /*! |
1549 | Returns true if the node \a n1 is less than node \a n2. |
1550 | The comparison is performed by comparing properties of the nodes in order |
1551 | of increasing complexity. |
1552 | */ |
1553 | bool compareNodes(const Node *n1, const Node *n2) |
1554 | { |
1555 | // Private nodes can occur in any order since they won't normally be |
1556 | // written to the index. |
1557 | if (n1->access() == Node::Private && n2->access() == Node::Private) |
1558 | return true; |
1559 | |
1560 | if (n1->location().filePath() < n2->location().filePath()) |
1561 | return true; |
1562 | else if (n1->location().filePath() > n2->location().filePath()) |
1563 | return false; |
1564 | |
1565 | if (n1->type() < n2->type()) |
1566 | return true; |
1567 | else if (n1->type() > n2->type()) |
1568 | return false; |
1569 | |
1570 | if (n1->name() < n2->name()) |
1571 | return true; |
1572 | else if (n1->name() > n2->name()) |
1573 | return false; |
1574 | |
1575 | if (n1->access() < n2->access()) |
1576 | return true; |
1577 | else if (n1->access() > n2->access()) |
1578 | return false; |
1579 | |
1580 | if (n1->type() == Node::Function && n2->type() == Node::Function) { |
1581 | const FunctionNode *f1 = static_cast<const FunctionNode *>(n1); |
1582 | const FunctionNode *f2 = static_cast<const FunctionNode *>(n2); |
1583 | |
1584 | if (f1->isConst() < f2->isConst()) |
1585 | return true; |
1586 | else if (f1->isConst() > f2->isConst()) |
1587 | return false; |
1588 | |
1589 | if (f1->signature() < f2->signature()) |
1590 | return true; |
1591 | else if (f1->signature() > f2->signature()) |
1592 | return false; |
1593 | } |
1594 | |
1595 | if (n1->type() == Node::Fake && n2->type() == Node::Fake) { |
1596 | const FakeNode *f1 = static_cast<const FakeNode *>(n1); |
1597 | const FakeNode *f2 = static_cast<const FakeNode *>(n2); |
1598 | if (f1->fullTitle() < f2->fullTitle()) |
1599 | return true; |
1600 | else if (f1->fullTitle() > f2->fullTitle()) |
1601 | return false; |
1602 | } |
1603 | |
1604 | return false; |
1605 | } |
1606 | |
1607 | /*! |
1608 | Generate index sections for the child nodes of the given \a node |
1609 | using the \a writer specified. If \a generateInternalNodes is true, |
1610 | nodes marked as internal will be included in the index; otherwise, |
1611 | they will be omitted. |
1612 | */ |
1613 | void Tree::generateIndexSections(QXmlStreamWriter &writer, |
1614 | const Node *node, |
1615 | bool generateInternalNodes) const |
1616 | { |
1617 | if (generateIndexSection(writer, node, generateInternalNodes)) { |
1618 | |
1619 | if (node->isInnerNode()) { |
1620 | const InnerNode *inner = static_cast<const InnerNode *>(node); |
1621 | |
1622 | NodeList cnodes = inner->childNodes(); |
1623 | qSort(cnodes.begin(), cnodes.end(), compareNodes); |
1624 | |
1625 | foreach (const Node *child, cnodes) { |
1626 | /* |
1627 | Don't generate anything for a QML property group node. |
1628 | It is just a place holder for a collection of QML property |
1629 | nodes. Recurse to its children, which are the QML property |
1630 | nodes. |
1631 | */ |
1632 | if (child->subType() == Node::QmlPropertyGroup) { |
1633 | const InnerNode *pgn = static_cast<const InnerNode*>(child); |
1634 | foreach (const Node *c, pgn->childNodes()) { |
1635 | generateIndexSections(writer, c, generateInternalNodes); |
1636 | } |
1637 | } |
1638 | else |
1639 | generateIndexSections(writer, child, generateInternalNodes); |
1640 | } |
1641 | |
1642 | /* |
1643 | foreach (const Node *child, inner->relatedNodes()) { |
1644 | QDomElement childElement = generateIndexSections(document, child); |
1645 | element.appendChild(childElement); |
1646 | } |
1647 | */ |
1648 | } |
1649 | writer.writeEndElement(); |
1650 | } |
1651 | } |
1652 | |
1653 | /*! |
1654 | Outputs an index file. |
1655 | */ |
1656 | void Tree::generateIndex(const QString &fileName, |
1657 | const QString &url, |
1658 | const QString &title, |
1659 | bool generateInternalNodes) const |
1660 | { |
1661 | QFile file(fileName); |
1662 | if (!file.open(QFile::WriteOnly | QFile::Text)) |
1663 | return ; |
1664 | |
1665 | QXmlStreamWriter writer(&file); |
1666 | writer.setAutoFormatting(true); |
1667 | writer.writeStartDocument(); |
1668 | writer.writeDTD("<!DOCTYPE QDOCINDEX>"); |
1669 | |
1670 | writer.writeStartElement("INDEX"); |
1671 | writer.writeAttribute("url", url); |
1672 | writer.writeAttribute("title", title); |
1673 | writer.writeAttribute("version", version()); |
1674 | |
1675 | generateIndexSections(writer, root(), generateInternalNodes); |
1676 | |
1677 | writer.writeEndElement(); // INDEX |
1678 | writer.writeEndElement(); // QDOCINDEX |
1679 | writer.writeEndDocument(); |
1680 | file.close(); |
1681 | } |
1682 | |
1683 | /*! |
1684 | Generate the tag file section with the given \a writer for the \a node |
1685 | specified, returning true if an element was written; otherwise returns |
1686 | false. |
1687 | */ |
1688 | void Tree::generateTagFileCompounds(QXmlStreamWriter &writer, |
1689 | const InnerNode *inner) const |
1690 | { |
1691 | foreach (const Node *node, inner->childNodes()) { |
1692 | |
1693 | if (!node->url().isEmpty()) |
1694 | continue; |
1695 | |
1696 | QString kind; |
1697 | switch (node->type()) { |
1698 | case Node::Namespace: |
1699 | kind = "namespace"; |
1700 | break; |
1701 | case Node::Class: |
1702 | kind = "class"; |
1703 | break; |
1704 | case Node::Enum: |
1705 | case Node::Typedef: |
1706 | case Node::Property: |
1707 | case Node::Function: |
1708 | case Node::Variable: |
1709 | case Node::Target: |
1710 | default: |
1711 | continue; |
1712 | } |
1713 | |
1714 | QString access; |
1715 | switch (node->access()) { |
1716 | case Node::Public: |
1717 | access = "public"; |
1718 | break; |
1719 | case Node::Protected: |
1720 | access = "protected"; |
1721 | break; |
1722 | case Node::Private: |
1723 | default: |
1724 | continue; |
1725 | } |
1726 | |
1727 | QString objName = node->name(); |
1728 | |
1729 | // Special case: only the root node should have an empty name. |
1730 | if (objName.isEmpty() && node != root()) |
1731 | continue; |
1732 | |
1733 | // *** Write the starting tag for the element here. *** |
1734 | writer.writeStartElement("compound"); |
1735 | writer.writeAttribute("kind", kind); |
1736 | |
1737 | if (node->type() == Node::Class) { |
1738 | writer.writeTextElement("name", fullDocumentName(node)); |
1739 | writer.writeTextElement("filename", HtmlGenerator::fullDocumentLocation(node)); |
1740 | |
1741 | // Classes contain information about their base classes. |
1742 | const ClassNode *classNode = static_cast<const ClassNode*>(node); |
1743 | QList<RelatedClass> bases = classNode->baseClasses(); |
1744 | foreach (const RelatedClass &related, bases) { |
1745 | ClassNode *baseClassNode = related.node; |
1746 | writer.writeTextElement("base", baseClassNode->name()); |
1747 | } |
1748 | |
1749 | // Recurse to write all members. |
1750 | generateTagFileMembers(writer, static_cast<const InnerNode *>(node)); |
1751 | writer.writeEndElement(); |
1752 | |
1753 | // Recurse to write all compounds. |
1754 | generateTagFileCompounds(writer, static_cast<const InnerNode *>(node)); |
1755 | } else { |
1756 | writer.writeTextElement("name", fullDocumentName(node)); |
1757 | writer.writeTextElement("filename", HtmlGenerator::fullDocumentLocation(node)); |
1758 | |
1759 | // Recurse to write all members. |
1760 | generateTagFileMembers(writer, static_cast<const InnerNode *>(node)); |
1761 | writer.writeEndElement(); |
1762 | |
1763 | // Recurse to write all compounds. |
1764 | generateTagFileCompounds(writer, static_cast<const InnerNode *>(node)); |
1765 | } |
1766 | } |
1767 | } |
1768 | |
1769 | /*! |
1770 | */ |
1771 | void Tree::generateTagFileMembers(QXmlStreamWriter &writer, |
1772 | const InnerNode *inner) const |
1773 | { |
1774 | foreach (const Node *node, inner->childNodes()) { |
1775 | |
1776 | if (!node->url().isEmpty()) |
1777 | continue; |
1778 | |
1779 | QString nodeName; |
1780 | QString kind; |
1781 | switch (node->type()) { |
1782 | case Node::Enum: |
1783 | nodeName = "member"; |
1784 | kind = "enum"; |
1785 | break; |
1786 | case Node::Typedef: |
1787 | nodeName = "member"; |
1788 | kind = "typedef"; |
1789 | break; |
1790 | case Node::Property: |
1791 | nodeName = "member"; |
1792 | kind = "property"; |
1793 | break; |
1794 | case Node::Function: |
1795 | nodeName = "member"; |
1796 | kind = "function"; |
1797 | break; |
1798 | case Node::Namespace: |
1799 | nodeName = "namespace"; |
1800 | break; |
1801 | case Node::Class: |
1802 | nodeName = "class"; |
1803 | break; |
1804 | case Node::Variable: |
1805 | case Node::Target: |
1806 | default: |
1807 | continue; |
1808 | } |
1809 | |
1810 | QString access; |
1811 | switch (node->access()) { |
1812 | case Node::Public: |
1813 | access = "public"; |
1814 | break; |
1815 | case Node::Protected: |
1816 | access = "protected"; |
1817 | break; |
1818 | case Node::Private: |
1819 | default: |
1820 | continue; |
1821 | } |
1822 | |
1823 | QString objName = node->name(); |
1824 | |
1825 | // Special case: only the root node should have an empty name. |
1826 | if (objName.isEmpty() && node != root()) |
1827 | continue; |
1828 | |
1829 | // *** Write the starting tag for the element here. *** |
1830 | writer.writeStartElement(nodeName); |
1831 | if (!kind.isEmpty()) |
1832 | writer.writeAttribute("kind", kind); |
1833 | |
1834 | switch (node->type()) { |
1835 | |
1836 | case Node::Class: |
1837 | writer.writeCharacters(fullDocumentName(node)); |
1838 | writer.writeEndElement(); |
1839 | break; |
1840 | case Node::Namespace: |
1841 | writer.writeCharacters(fullDocumentName(node)); |
1842 | writer.writeEndElement(); |
1843 | break; |
1844 | case Node::Function: |
1845 | { |
1846 | /* |
1847 | Function nodes contain information about |
1848 | the type of function being described. |
1849 | */ |
1850 | |
1851 | const FunctionNode *functionNode = |
1852 | static_cast<const FunctionNode*>(node); |
1853 | writer.writeAttribute("protection", access); |
1854 | |
1855 | switch (functionNode->virtualness()) { |
1856 | case FunctionNode::NonVirtual: |
1857 | writer.writeAttribute("virtualness", "non"); |
1858 | break; |
1859 | case FunctionNode::ImpureVirtual: |
1860 | writer.writeAttribute("virtualness", "virtual"); |
1861 | break; |
1862 | case FunctionNode::PureVirtual: |
1863 | writer.writeAttribute("virtual", "pure"); |
1864 | break; |
1865 | default: |
1866 | break; |
1867 | } |
1868 | writer.writeAttribute("static", |
1869 | functionNode->isStatic() ? "yes": "no"); |
1870 | |
1871 | if (functionNode->virtualness() == FunctionNode::NonVirtual) |
1872 | writer.writeTextElement("type", functionNode->returnType()); |
1873 | else |
1874 | writer.writeTextElement("type", |
1875 | "virtual "+ functionNode->returnType()); |
1876 | |
1877 | writer.writeTextElement("name", objName); |
1878 | QStringList pieces = HtmlGenerator::fullDocumentLocation(node).split("#"); |
1879 | writer.writeTextElement("anchorfile", pieces[0]); |
1880 | writer.writeTextElement("anchor", pieces[1]); |
1881 | |
1882 | // Write a signature attribute for convenience. |
1883 | QStringList signatureList; |
1884 | |
1885 | foreach (const Parameter ¶meter, functionNode->parameters()) { |
1886 | QString leftType = parameter.leftType(); |
1887 | const Node *leftNode = const_cast<Tree *>(this)->findNode(parameter.leftType().split("::"), |
1888 | Node::Typedef, 0, SearchBaseClasses|NonFunction); |
1889 | if (!leftNode) { |
1890 | leftNode = const_cast<Tree *>(this)->findNode( |
1891 | parameter.leftType().split("::"), Node::Typedef, |
1892 | node->parent(), SearchBaseClasses|NonFunction); |
1893 | } |
1894 | if (leftNode) { |
1895 | const TypedefNode *typedefNode = static_cast<const TypedefNode *>(leftNode); |
1896 | if (typedefNode->associatedEnum()) { |
1897 | leftType = "QFlags<"+fullDocumentName(typedefNode->associatedEnum())+ ">"; |
1898 | } |
1899 | } |
1900 | signatureList.append(leftType + " "+ parameter.name()); |
1901 | } |
1902 | |
1903 | QString signature = "("+signatureList.join( ", ")+ ")"; |
1904 | if (functionNode->isConst()) |
1905 | signature += " const"; |
1906 | if (functionNode->virtualness() == FunctionNode::PureVirtual) |
1907 | signature += " = 0"; |
1908 | writer.writeTextElement("arglist", signature); |
1909 | } |
1910 | writer.writeEndElement(); // member |
1911 | break; |
1912 | |
1913 | case Node::Property: |
1914 | { |
1915 | const PropertyNode *propertyNode = static_cast<const PropertyNode*>(node); |
1916 | writer.writeAttribute("type", propertyNode->dataType()); |
1917 | writer.writeTextElement("name", objName); |
1918 | QStringList pieces = HtmlGenerator::fullDocumentLocation(node).split("#"); |
1919 | writer.writeTextElement("anchorfile", pieces[0]); |
1920 | writer.writeTextElement("anchor", pieces[1]); |
1921 | writer.writeTextElement("arglist", ""); |
1922 | } |
1923 | writer.writeEndElement(); // member |
1924 | break; |
1925 | |
1926 | case Node::Enum: |
1927 | { |
1928 | const EnumNode *enumNode = static_cast<const EnumNode*>(node); |
1929 | writer.writeTextElement("name", objName); |
1930 | QStringList pieces = HtmlGenerator::fullDocumentLocation(node).split("#"); |
1931 | writer.writeTextElement("anchor", pieces[1]); |
1932 | writer.writeTextElement("arglist", ""); |
1933 | writer.writeEndElement(); // member |
1934 | |
1935 | for (int i = 0; i < enumNode->items().size(); ++i) { |
1936 | EnumItem item = enumNode->items().value(i); |
1937 | writer.writeStartElement("member"); |
1938 | writer.writeAttribute("name", item.name()); |
1939 | writer.writeTextElement("anchor", pieces[1]); |
1940 | writer.writeTextElement("arglist", ""); |
1941 | writer.writeEndElement(); // member |
1942 | } |
1943 | } |
1944 | break; |
1945 | |
1946 | case Node::Typedef: |
1947 | { |
1948 | const TypedefNode *typedefNode = static_cast<const TypedefNode*>(node); |
1949 | if (typedefNode->associatedEnum()) |
1950 | writer.writeAttribute("type", fullDocumentName(typedefNode->associatedEnum())); |
1951 | else |
1952 | writer.writeAttribute("type", ""); |
1953 | writer.writeTextElement("name", objName); |
1954 | QStringList pieces = HtmlGenerator::fullDocumentLocation(node).split("#"); |
1955 | writer.writeTextElement("anchorfile", pieces[0]); |
1956 | writer.writeTextElement("anchor", pieces[1]); |
1957 | writer.writeTextElement("arglist", ""); |
1958 | } |
1959 | writer.writeEndElement(); // member |
1960 | break; |
1961 | |
1962 | case Node::Variable: |
1963 | case Node::Target: |
1964 | default: |
1965 | break; |
1966 | } |
1967 | } |
1968 | } |
1969 | |
1970 | /*! |
1971 | */ |
1972 | void Tree::generateTagFile(const QString &fileName) const |
1973 | { |
1974 | QFile file(fileName); |
1975 | if (!file.open(QFile::WriteOnly | QFile::Text)) |
1976 | return ; |
1977 | |
1978 | QXmlStreamWriter writer(&file); |
1979 | writer.setAutoFormatting(true); |
1980 | writer.writeStartDocument(); |
1981 | |
1982 | writer.writeStartElement("tagfile"); |
1983 | |
1984 | generateTagFileCompounds(writer, root()); |
1985 | |
1986 | writer.writeEndElement(); // tagfile |
1987 | writer.writeEndDocument(); |
1988 | file.close(); |
1989 | } |
1990 | |
1991 | /*! |
1992 | */ |
1993 | void Tree::addExternalLink(const QString &url, const Node *relative) |
1994 | { |
1995 | FakeNode *fakeNode = new FakeNode(root(), url, Node::ExternalPage); |
1996 | fakeNode->setAccess(Node::Public); |
1997 | |
1998 | // Create some content for the node. |
1999 | QSet<QString> emptySet; |
2000 | Location location(relative->doc().location()); |
2001 | Doc doc(location, location, " ", emptySet); // placeholder |
2002 | fakeNode->setDoc(doc); |
2003 | } |
2004 | |
2005 | /*! |
2006 | Construct the full document name for \a node and return the |
2007 | name. |
2008 | */ |
2009 | QString Tree::fullDocumentName(const Node *node) const |
2010 | { |
2011 | if (!node) |
2012 | return ""; |
2013 | |
2014 | QStringList pieces; |
2015 | const Node *n = node; |
2016 | |
2017 | do { |
2018 | if (!n->name().isEmpty() && |
2019 | ((n->type() != Node::Fake) || (n->subType() != Node::QmlPropertyGroup))) |
2020 | pieces.insert(0, n->name()); |
2021 | |
2022 | if ((n->type() == Node::Fake) && (n->subType() != Node::QmlPropertyGroup)) |
2023 | break; |
2024 | |
2025 | // Examine the parent node if one exists. |
2026 | if (n->parent()) |
2027 | n = n->parent(); |
2028 | else |
2029 | break; |
2030 | } while (true); |
2031 | |
2032 | // Create a name based on the type of the ancestor node. |
2033 | if (n->type() == Node::Fake) |
2034 | return pieces.join("#"); |
2035 | else |
2036 | return pieces.join("::"); |
2037 | } |
2038 | |
2039 | QT_END_NAMESPACE |
2040 |
Warning: That file was not part of the compilation database. It may have many parsing errors.