1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the tools applications of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
43 #include "qxmlstream.h"
44 #include "qdocindexfiles.h"
45 #include "qdoctagfiles.h"
47 #include "qdocdatabase.h"
50 #include "generator.h"
58 This class handles qdoc index files.
61 QDocIndexFiles* QDocIndexFiles::qdocIndexFiles_ = NULL;
64 Constructs the singleton QDocIndexFiles.
66 QDocIndexFiles::QDocIndexFiles()
68 qdb_ = QDocDatabase::qdocDB();
72 Destroys the singleton QDocIndexFiles.
74 QDocIndexFiles::~QDocIndexFiles()
80 Creates the singleton. Allows only one instance of the class
81 to be created. Returns a pointer to the singleton.
83 QDocIndexFiles* QDocIndexFiles::qdocIndexFiles()
86 qdocIndexFiles_ = new QDocIndexFiles;
87 return qdocIndexFiles_;
91 Destroys the singleton.
93 void QDocIndexFiles::destroyQDocIndexFiles()
95 if (qdocIndexFiles_) {
96 delete qdocIndexFiles_;
102 Reads and parses the list of index files in \a indexFiles.
104 void QDocIndexFiles::readIndexes(const QStringList& indexFiles)
106 foreach (const QString& indexFile, indexFiles) {
107 QString msg = "Loading index file: " + indexFile;
108 Location::logToStdErr(msg);
109 readIndexFile(indexFile);
114 Reads and parses the index file at \a path.
116 void QDocIndexFiles::readIndexFile(const QString& path)
119 if (file.open(QFile::ReadOnly)) {
120 QDomDocument document;
121 document.setContent(&file);
124 QDomElement indexElement = document.documentElement();
126 // Generate a relative URL between the install dir and the index file
127 // when the -installdir command line option is set.
129 if (Config::installDir.isEmpty()) {
130 indexUrl = indexElement.attribute("url", QString());
133 // Use a fake directory, since we will copy the output to a sub directory of
134 // installDir when using "make install". This is just for a proper relative path.
135 QDir installDir(path.section('/', 0, -3) + "/outputdir");
136 indexUrl = installDir.relativeFilePath(path).section('/', 0, -2);
140 relatedList_.clear();
142 // Scan all elements in the XML file, constructing a map that contains
143 // base classes for each class found.
145 QDomElement child = indexElement.firstChildElement();
146 while (!child.isNull()) {
147 readIndexSection(child, qdb_->treeRoot(), indexUrl);
148 child = child.nextSiblingElement();
151 // Now that all the base classes have been found for this index,
152 // arrange them into an inheritance hierarchy.
158 Read a <section> element from the index file and create the
161 void QDocIndexFiles::readIndexSection(const QDomElement& element,
163 const QString& indexUrl)
165 QString name = element.attribute("name");
166 QString href = element.attribute("href");
170 if (element.nodeName() == "namespace") {
171 node = new NamespaceNode(parent, name);
173 if (!indexUrl.isEmpty())
174 location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
175 else if (!indexUrl.isNull())
176 location = Location(name.toLower() + ".html");
179 else if (element.nodeName() == "class") {
180 node = new ClassNode(parent, name);
181 basesList_.append(QPair<ClassNode*,QString>(static_cast<ClassNode*>(node),
182 element.attribute("bases")));
184 if (!indexUrl.isEmpty())
185 location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
186 else if (!indexUrl.isNull())
187 location = Location(name.toLower() + ".html");
189 else if ((element.nodeName() == "qmlclass") ||
190 ((element.nodeName() == "page") && (element.attribute("subtype") == "qmlclass"))) {
191 QmlClassNode* qcn = new QmlClassNode(parent, name);
192 qcn->setTitle(element.attribute("title"));
193 if (element.hasAttribute("location"))
194 name = element.attribute("location", QString());
195 if (!indexUrl.isEmpty())
196 location = Location(indexUrl + QLatin1Char('/') + name);
197 else if (!indexUrl.isNull())
198 location = Location(name);
201 else if (element.nodeName() == "qmlbasictype") {
202 QmlBasicTypeNode* qbtn = new QmlBasicTypeNode(parent, name);
203 qbtn->setTitle(element.attribute("title"));
204 if (element.hasAttribute("location"))
205 name = element.attribute("location", QString());
206 if (!indexUrl.isEmpty())
207 location = Location(indexUrl + QLatin1Char('/') + name);
208 else if (!indexUrl.isNull())
209 location = Location(name);
212 else if (element.nodeName() == "page") {
213 Node::SubType subtype;
214 Node::PageType ptype = Node::NoPageType;
215 if (element.attribute("subtype") == "example") {
216 subtype = Node::Example;
217 ptype = Node::ExamplePage;
219 else if (element.attribute("subtype") == "header") {
220 subtype = Node::HeaderFile;
221 ptype = Node::ApiPage;
223 else if (element.attribute("subtype") == "file") {
224 subtype = Node::File;
225 ptype = Node::NoPageType;
227 else if (element.attribute("subtype") == "group") {
228 subtype = Node::Group;
229 ptype = Node::OverviewPage;
231 else if (element.attribute("subtype") == "module") {
232 subtype = Node::Module;
233 ptype = Node::OverviewPage;
235 else if (element.attribute("subtype") == "qmlmodule") {
236 subtype = Node::QmlModule;
237 ptype = Node::OverviewPage;
239 else if (element.attribute("subtype") == "page") {
240 subtype = Node::Page;
241 ptype = Node::ArticlePage;
243 else if (element.attribute("subtype") == "externalpage") {
244 subtype = Node::ExternalPage;
245 ptype = Node::ArticlePage;
247 else if (element.attribute("subtype") == "qmlclass") {
248 subtype = Node::QmlClass;
249 ptype = Node::ApiPage;
251 else if (element.attribute("subtype") == "qmlpropertygroup") {
252 subtype = Node::QmlPropertyGroup;
253 ptype = Node::ApiPage;
255 else if (element.attribute("subtype") == "qmlbasictype") {
256 subtype = Node::QmlBasicType;
257 ptype = Node::ApiPage;
262 DocNode* docNode = new DocNode(parent, name, subtype, ptype);
263 docNode->setTitle(element.attribute("title"));
265 if (element.hasAttribute("location"))
266 name = element.attribute("location", QString());
268 if (!indexUrl.isEmpty())
269 location = Location(indexUrl + QLatin1Char('/') + name);
270 else if (!indexUrl.isNull())
271 location = Location(name);
276 else if (element.nodeName() == "enum") {
277 EnumNode* enumNode = new EnumNode(parent, name);
279 if (!indexUrl.isEmpty())
280 location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
281 else if (!indexUrl.isNull())
282 location = Location(parent->name().toLower() + ".html");
284 QDomElement child = element.firstChildElement("value");
285 while (!child.isNull()) {
286 EnumItem item(child.attribute("name"), child.attribute("value"));
287 enumNode->addItem(item);
288 child = child.nextSiblingElement("value");
294 else if (element.nodeName() == "typedef") {
295 node = new TypedefNode(parent, name);
297 if (!indexUrl.isEmpty())
298 location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
299 else if (!indexUrl.isNull())
300 location = Location(parent->name().toLower() + ".html");
303 else if (element.nodeName() == "property") {
304 node = new PropertyNode(parent, name);
306 if (!indexUrl.isEmpty())
307 location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
308 else if (!indexUrl.isNull())
309 location = Location(parent->name().toLower() + ".html");
312 else if (element.nodeName() == "function") {
313 FunctionNode::Virtualness virt;
314 if (element.attribute("virtual") == "non")
315 virt = FunctionNode::NonVirtual;
316 else if (element.attribute("virtual") == "impure")
317 virt = FunctionNode::ImpureVirtual;
318 else if (element.attribute("virtual") == "pure")
319 virt = FunctionNode::PureVirtual;
323 FunctionNode::Metaness meta;
324 if (element.attribute("meta") == "plain")
325 meta = FunctionNode::Plain;
326 else if (element.attribute("meta") == "signal")
327 meta = FunctionNode::Signal;
328 else if (element.attribute("meta") == "slot")
329 meta = FunctionNode::Slot;
330 else if (element.attribute("meta") == "constructor")
331 meta = FunctionNode::Ctor;
332 else if (element.attribute("meta") == "destructor")
333 meta = FunctionNode::Dtor;
334 else if (element.attribute("meta") == "macro")
335 meta = FunctionNode::MacroWithParams;
336 else if (element.attribute("meta") == "macrowithparams")
337 meta = FunctionNode::MacroWithParams;
338 else if (element.attribute("meta") == "macrowithoutparams")
339 meta = FunctionNode::MacroWithoutParams;
343 FunctionNode* functionNode = new FunctionNode(parent, name);
344 functionNode->setReturnType(element.attribute("return"));
345 functionNode->setVirtualness(virt);
346 functionNode->setMetaness(meta);
347 functionNode->setConst(element.attribute("const") == "true");
348 functionNode->setStatic(element.attribute("static") == "true");
349 functionNode->setOverload(element.attribute("overload") == "true");
351 if (element.hasAttribute("relates")
352 && element.attribute("relates") != parent->name()) {
354 QPair<FunctionNode*,QString>(functionNode,
355 element.attribute("relates")));
358 QDomElement child = element.firstChildElement("parameter");
359 while (!child.isNull()) {
360 // Do not use the default value for the parameter; it is not
361 // required, and has been known to cause problems.
362 Parameter parameter(child.attribute("left"),
363 child.attribute("right"),
364 child.attribute("name"),
365 QString()); // child.attribute("default")
366 functionNode->addParameter(parameter);
367 child = child.nextSiblingElement("parameter");
371 if (!indexUrl.isEmpty())
372 location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
373 else if (!indexUrl.isNull())
374 location = Location(parent->name().toLower() + ".html");
376 else if (element.nodeName() == "variable") {
377 node = new VariableNode(parent, name);
378 if (!indexUrl.isEmpty())
379 location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
380 else if (!indexUrl.isNull())
381 location = Location(parent->name().toLower() + ".html");
383 else if (element.nodeName() == "keyword") {
384 qdb_->insertTarget(name, TargetRec::Keyword, parent, 1);
387 else if (element.nodeName() == "target") {
388 qdb_->insertTarget(name, TargetRec::Target, parent, 2);
391 else if (element.nodeName() == "contents") {
392 qdb_->insertTarget(name, TargetRec::Contents, parent, 3);
398 QString access = element.attribute("access");
399 if (access == "public")
400 node->setAccess(Node::Public);
401 else if (access == "protected")
402 node->setAccess(Node::Protected);
403 else if (access == "private")
404 node->setAccess(Node::Private);
406 node->setAccess(Node::Public);
408 if ((element.nodeName() != "page") &&
409 (element.nodeName() != "qmlclass") &&
410 (element.nodeName() != "qmlbasictype")) {
411 QString threadSafety = element.attribute("threadsafety");
412 if (threadSafety == "non-reentrant")
413 node->setThreadSafeness(Node::NonReentrant);
414 else if (threadSafety == "reentrant")
415 node->setThreadSafeness(Node::Reentrant);
416 else if (threadSafety == "thread safe")
417 node->setThreadSafeness(Node::ThreadSafe);
419 node->setThreadSafeness(Node::UnspecifiedSafeness);
422 node->setThreadSafeness(Node::UnspecifiedSafeness);
424 QString status = element.attribute("status");
425 if (status == "compat")
426 node->setStatus(Node::Compat);
427 else if (status == "obsolete")
428 node->setStatus(Node::Obsolete);
429 else if (status == "deprecated")
430 node->setStatus(Node::Deprecated);
431 else if (status == "preliminary")
432 node->setStatus(Node::Preliminary);
433 else if (status == "commendable")
434 node->setStatus(Node::Commendable);
435 else if (status == "internal")
436 node->setStatus(Node::Internal);
437 else if (status == "main")
438 node->setStatus(Node::Main);
440 node->setStatus(Node::Commendable);
442 QString moduleName = element.attribute("module");
443 if (!moduleName.isEmpty())
444 node->setModuleName(moduleName);
445 if (!indexUrl.isEmpty()) {
446 node->setUrl(indexUrl + QLatin1Char('/') + href);
449 QString since = element.attribute("since");
450 if (!since.isEmpty()) {
451 node->setSince(since);
454 QString groupsAttr = element.attribute("groups");
455 if (!groupsAttr.isEmpty()) {
456 QStringList groupNames = groupsAttr.split(",");
457 for (int i=0; i<groupNames.size(); ++i) {
458 DocNode* dn = qdb_->findGroup(groupNames[i]);
463 qDebug() << "NODE:" << node->name() << "GROUPS:" << groupNames;
464 qDebug() << "DID NOT FIND GROUP:" << dn->name() << "for:" << node->name();
469 // Create some content for the node.
470 QSet<QString> emptySet;
471 Doc doc(location, location, " ", emptySet); // placeholder
473 node->setIndexNodeFlag();
475 if (node->isInnerNode()) {
476 InnerNode* inner = static_cast<InnerNode*>(node);
477 QDomElement child = element.firstChildElement();
478 while (!child.isNull()) {
479 if (element.nodeName() == "class")
480 readIndexSection(child, inner, indexUrl);
481 else if (element.nodeName() == "qmlclass")
482 readIndexSection(child, inner, indexUrl);
483 else if (element.nodeName() == "page")
484 readIndexSection(child, inner, indexUrl);
485 else if (element.nodeName() == "namespace" && !name.isEmpty())
486 // The root node in the index is a namespace with an empty name.
487 readIndexSection(child, inner, indexUrl);
489 readIndexSection(child, parent, indexUrl);
490 child = child.nextSiblingElement();
497 void QDocIndexFiles::resolveIndex()
499 QPair<ClassNode*,QString> pair;
500 foreach (pair, basesList_) {
501 foreach (const QString& base, pair.second.split(QLatin1Char(','))) {
502 Node* n = qdb_->treeRoot()->findChildNodeByNameAndType(base, Node::Class);
504 pair.first->addBaseClass(Node::Public, static_cast<ClassNode*>(n));
509 QPair<FunctionNode*,QString> relatedPair;
510 foreach (relatedPair, relatedList_) {
511 Node* n = qdb_->treeRoot()->findChildNodeByNameAndType(relatedPair.second, Node::Class);
513 relatedPair.first->setRelates(static_cast<ClassNode*>(n));
518 Normally this is used for writing the \e groups attribute,
519 but it can be used for writing any attribute with a list
520 value that comes from some subset of the members of \a n.
522 \note The members of \a n are \e not the children of \a n.
524 The names we want to include are the names of the members
525 of \a n that have node type \a t and node subtype \a st.
526 The attribute name is \a attr. The names are joined with
527 the space character and written with \a writer.
529 void QDocIndexFiles::writeMembersAttribute(QXmlStreamWriter& writer,
535 const NodeList& members = n->members();
536 if (!members.isEmpty()) {
538 NodeList::ConstIterator i = members.constBegin();
539 while (i != members.constEnd()) {
540 if ((*i)->type() == t && (*i)->subType() == st)
541 names.append((*i)->name());
544 if (!names.isEmpty())
545 writer.writeAttribute(attr, names.join(","));
550 Generate the index section with the given \a writer for the \a node
551 specified, returning true if an element was written; otherwise returns
554 bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
556 bool generateInternalNodes)
558 if (node->subType() == Node::DitaMap)
562 switch (node->type()) {
563 case Node::Namespace:
564 nodeName = "namespace";
571 if (node->subType() == Node::QmlClass)
572 nodeName = "qmlclass";
573 else if (node->subType() == Node::QmlBasicType)
574 nodeName = "qmlbasictype";
580 nodeName = "typedef";
583 nodeName = "property";
586 nodeName = "function";
589 nodeName = "variable";
591 case Node::QmlProperty:
592 nodeName = "qmlproperty";
594 case Node::QmlSignal:
595 nodeName = "qmlsignal";
597 case Node::QmlSignalHandler:
598 nodeName = "qmlsignalhandler";
600 case Node::QmlMethod:
601 nodeName = "qmlmethod";
608 switch (node->access()) {
612 case Node::Protected:
613 access = "protected";
616 // Do not include private non-internal nodes in the index.
617 // (Internal public and protected nodes are marked as private
618 // by qdoc. We can check their internal status to determine
619 // whether they were really private to begin with.)
620 if (node->status() == Node::Internal && generateInternalNodes)
629 QString objName = node->name();
630 // Special case: only the root node should have an empty name.
631 if (objName.isEmpty() && node != qdb_->treeRoot())
634 writer.writeStartElement(nodeName);
636 QXmlStreamAttributes attributes;
637 writer.writeAttribute("access", access);
639 if (node->type() != Node::Document) {
640 QString threadSafety;
641 switch (node->threadSafeness()) {
642 case Node::NonReentrant:
643 threadSafety = "non-reentrant";
645 case Node::Reentrant:
646 threadSafety = "reentrant";
648 case Node::ThreadSafe:
649 threadSafety = "thread safe";
651 case Node::UnspecifiedSafeness:
653 threadSafety = "unspecified";
656 writer.writeAttribute("threadsafety", threadSafety);
660 switch (node->status()) {
667 case Node::Deprecated:
668 status = "deprecated";
670 case Node::Preliminary:
671 status = "preliminary";
673 case Node::Commendable:
674 status = "commendable";
684 writer.writeAttribute("status", status);
686 writer.writeAttribute("name", objName);
687 QString fullName = node->fullDocumentName();
688 if (fullName != objName)
689 writer.writeAttribute("fullname", fullName);
690 QString href = node->outputSubdirectory();
692 href.append(QLatin1Char('/'));
693 href.append(gen_->fullDocumentLocation(node));
694 writer.writeAttribute("href", href);
695 if ((node->type() != Node::Document) && (!node->isQmlNode()))
696 writer.writeAttribute("location", node->location().fileName());
698 if (!node->since().isEmpty()) {
699 writer.writeAttribute("since", node->since());
702 switch (node->type()) {
705 // Classes contain information about their base classes.
706 const ClassNode* classNode = static_cast<const ClassNode*>(node);
707 QList<RelatedClass> bases = classNode->baseClasses();
708 QSet<QString> baseStrings;
709 foreach (const RelatedClass& related, bases) {
710 ClassNode* baseClassNode = related.node;
711 baseStrings.insert(baseClassNode->name());
713 writer.writeAttribute("bases", QStringList(baseStrings.toList()).join(","));
714 if (!node->moduleName().isEmpty())
715 writer.writeAttribute("module", node->moduleName());
716 writeMembersAttribute(writer, classNode, Node::Document, Node::Group, "groups");
719 case Node::Namespace:
721 const NamespaceNode* namespaceNode = static_cast<const NamespaceNode*>(node);
722 if (!namespaceNode->moduleName().isEmpty())
723 writer.writeAttribute("module", namespaceNode->moduleName());
724 writeMembersAttribute(writer, namespaceNode, Node::Document, Node::Group, "groups");
730 Document nodes (such as manual pages) contain subtypes,
731 titles and other attributes.
733 bool writeModuleName = false;
734 const DocNode* docNode = static_cast<const DocNode*>(node);
735 switch (docNode->subType()) {
737 writer.writeAttribute("subtype", "example");
738 writeModuleName = true;
740 case Node::HeaderFile:
741 writer.writeAttribute("subtype", "header");
742 writeModuleName = true;
745 writer.writeAttribute("subtype", "file");
749 writer.writeAttribute("subtype", "group");
750 writer.writeAttribute("seen", docNode->wasSeen() ? "true" : "false");
751 // Groups contain information about their group members.
752 const NodeList& members = docNode->members();
754 foreach (const Node* member, members) {
755 names.append(member->name());
757 writer.writeAttribute("members", names.join(","));
758 writeModuleName = true;
762 writer.writeAttribute("subtype", "module");
764 case Node::QmlModule:
765 writer.writeAttribute("subtype", "qmlmodule");
768 writer.writeAttribute("subtype", "page");
769 writeModuleName = true;
771 case Node::ExternalPage:
772 writer.writeAttribute("subtype", "externalpage");
775 //writer.writeAttribute("subtype", "qmlclass");
777 case Node::QmlBasicType:
778 //writer.writeAttribute("subtype", "qmlbasictype");
783 writer.writeAttribute("title", docNode->title());
784 writer.writeAttribute("fulltitle", docNode->fullTitle());
785 writer.writeAttribute("subtitle", docNode->subTitle());
786 writer.writeAttribute("location", docNode->doc().location().fileName());
787 if (!node->moduleName().isEmpty() && writeModuleName) {
788 writer.writeAttribute("module", node->moduleName());
790 writeMembersAttribute(writer, docNode, Node::Document, Node::Group, "groups");
796 Function nodes contain information about the type of
797 function being described.
799 const FunctionNode* functionNode = static_cast<const FunctionNode*>(node);
800 switch (functionNode->virtualness()) {
801 case FunctionNode::NonVirtual:
802 writer.writeAttribute("virtual", "non");
804 case FunctionNode::ImpureVirtual:
805 writer.writeAttribute("virtual", "impure");
807 case FunctionNode::PureVirtual:
808 writer.writeAttribute("virtual", "pure");
814 switch (functionNode->metaness()) {
815 case FunctionNode::Plain:
816 writer.writeAttribute("meta", "plain");
818 case FunctionNode::Signal:
819 writer.writeAttribute("meta", "signal");
821 case FunctionNode::Slot:
822 writer.writeAttribute("meta", "slot");
824 case FunctionNode::Ctor:
825 writer.writeAttribute("meta", "constructor");
827 case FunctionNode::Dtor:
828 writer.writeAttribute("meta", "destructor");
830 case FunctionNode::MacroWithParams:
831 writer.writeAttribute("meta", "macrowithparams");
833 case FunctionNode::MacroWithoutParams:
834 writer.writeAttribute("meta", "macrowithoutparams");
839 writer.writeAttribute("const", functionNode->isConst()?"true":"false");
840 writer.writeAttribute("static", functionNode->isStatic()?"true":"false");
841 writer.writeAttribute("overload", functionNode->isOverload()?"true":"false");
842 if (functionNode->isOverload())
843 writer.writeAttribute("overload-number", QString::number(functionNode->overloadNumber()));
844 if (functionNode->relates())
845 writer.writeAttribute("relates", functionNode->relates()->name());
846 const PropertyNode* propertyNode = functionNode->associatedProperty();
848 writer.writeAttribute("associated-property", propertyNode->name());
849 writer.writeAttribute("type", functionNode->returnType());
852 case Node::QmlProperty:
854 QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node);
855 writer.writeAttribute("type", qpn->dataType());
856 writer.writeAttribute("attached", qpn->isAttached() ? "true" : "false");
857 writer.writeAttribute("writable", qpn->isWritable(qdb_) ? "true" : "false");
862 const PropertyNode* propertyNode = static_cast<const PropertyNode*>(node);
863 writer.writeAttribute("type", propertyNode->dataType());
864 foreach (const Node* fnNode, propertyNode->getters()) {
866 const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
867 writer.writeStartElement("getter");
868 writer.writeAttribute("name", functionNode->name());
869 writer.writeEndElement(); // getter
872 foreach (const Node* fnNode, propertyNode->setters()) {
874 const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
875 writer.writeStartElement("setter");
876 writer.writeAttribute("name", functionNode->name());
877 writer.writeEndElement(); // setter
880 foreach (const Node* fnNode, propertyNode->resetters()) {
882 const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
883 writer.writeStartElement("resetter");
884 writer.writeAttribute("name", functionNode->name());
885 writer.writeEndElement(); // resetter
888 foreach (const Node* fnNode, propertyNode->notifiers()) {
890 const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode);
891 writer.writeStartElement("notifier");
892 writer.writeAttribute("name", functionNode->name());
893 writer.writeEndElement(); // notifier
900 const VariableNode* variableNode = static_cast<const VariableNode*>(node);
901 writer.writeAttribute("type", variableNode->dataType());
902 writer.writeAttribute("static", variableNode->isStatic() ? "true" : "false");
909 // Inner nodes and function nodes contain child nodes of some sort, either
910 // actual child nodes or function parameters. For these, we close the
911 // opening tag, create child elements, then add a closing tag for the
912 // element. Elements for all other nodes are closed in the opening tag.
914 if (node->isInnerNode()) {
915 const InnerNode* inner = static_cast<const InnerNode*>(node);
917 // For internal pages, we canonicalize the target, keyword and content
918 // item names so that they can be used by qdoc for other sets of
920 // The reason we do this here is that we don't want to ruin
921 // externally composed indexes, containing non-qdoc-style target names
922 // when reading in indexes.
924 if (inner->doc().hasTargets()) {
925 bool external = false;
926 if (inner->type() == Node::Document) {
927 const DocNode* docNode = static_cast<const DocNode*>(inner);
928 if (docNode->subType() == Node::ExternalPage)
931 foreach (const Atom* target, inner->doc().targets()) {
932 QString targetName = target->string();
934 targetName = Doc::canonicalTitle(targetName);
935 writer.writeStartElement("target");
936 writer.writeAttribute("name", targetName);
937 writer.writeEndElement(); // target
940 if (inner->doc().hasKeywords()) {
941 foreach (const Atom* keyword, inner->doc().keywords()) {
942 writer.writeStartElement("keyword");
943 writer.writeAttribute("name", Doc::canonicalTitle(keyword->string()));
944 writer.writeEndElement(); // keyword
947 if (inner->doc().hasTableOfContents()) {
948 for (int i = 0; i < inner->doc().tableOfContents().size(); ++i) {
949 Atom* item = inner->doc().tableOfContents()[i];
950 int level = inner->doc().tableOfContentsLevels()[i];
951 QString title = Text::sectionHeading(item).toString();
952 writer.writeStartElement("contents");
953 writer.writeAttribute("name", Doc::canonicalTitle(title));
954 writer.writeAttribute("title", title);
955 writer.writeAttribute("level", QString::number(level));
956 writer.writeEndElement(); // contents
960 else if (node->type() == Node::Function) {
961 const FunctionNode* functionNode = static_cast<const FunctionNode*>(node);
962 // Write a signature attribute for convenience.
963 QStringList signatureList;
964 QStringList resolvedParameters;
965 foreach (const Parameter& parameter, functionNode->parameters()) {
966 QString leftType = parameter.leftType();
967 const Node* leftNode = qdb_->findNode(parameter.leftType().split("::"),
969 SearchBaseClasses|NonFunction);
970 if (!leftNode || leftNode->type() != Node::Typedef) {
971 leftNode = qdb_->findNode(parameter.leftType().split("::"),
973 SearchBaseClasses|NonFunction);
975 if (leftNode && leftNode->type() == Node::Typedef) {
976 if (leftNode->type() == Node::Typedef) {
977 const TypedefNode* typedefNode = static_cast<const TypedefNode*>(leftNode);
978 if (typedefNode->associatedEnum()) {
979 leftType = "QFlags<" + typedefNode->associatedEnum()->fullDocumentName() +
984 leftType = leftNode->fullDocumentName();
986 resolvedParameters.append(leftType);
987 signatureList.append(leftType + QLatin1Char(' ') + parameter.name());
990 QString signature = functionNode->name() + QLatin1Char('(') + signatureList.join(", ") +
992 if (functionNode->isConst())
993 signature += " const";
994 writer.writeAttribute("signature", signature);
996 for (int i = 0; i < functionNode->parameters().size(); ++i) {
997 Parameter parameter = functionNode->parameters()[i];
998 writer.writeStartElement("parameter");
999 writer.writeAttribute("left", resolvedParameters[i]);
1000 writer.writeAttribute("right", parameter.rightType());
1001 writer.writeAttribute("name", parameter.name());
1002 writer.writeAttribute("default", parameter.defaultValue());
1003 writer.writeEndElement(); // parameter
1006 else if (node->type() == Node::Enum) {
1007 const EnumNode* enumNode = static_cast<const EnumNode*>(node);
1008 if (enumNode->flagsType()) {
1009 writer.writeAttribute("typedef",enumNode->flagsType()->fullDocumentName());
1011 foreach (const EnumItem& item, enumNode->items()) {
1012 writer.writeStartElement("value");
1013 writer.writeAttribute("name", item.name());
1014 writer.writeAttribute("value", item.value());
1015 writer.writeEndElement(); // value
1018 else if (node->type() == Node::Typedef) {
1019 const TypedefNode* typedefNode = static_cast<const TypedefNode*>(node);
1020 if (typedefNode->associatedEnum()) {
1021 writer.writeAttribute("enum",typedefNode->associatedEnum()->fullDocumentName());
1028 Returns true if the node \a n1 is less than node \a n2. The
1029 comparison is performed by comparing properties of the nodes
1030 in order of increasing complexity.
1032 bool compareNodes(const Node* n1, const Node* n2)
1034 // Private nodes can occur in any order since they won't normally be
1035 // written to the index.
1036 if (n1->access() == Node::Private && n2->access() == Node::Private)
1039 if (n1->location().filePath() < n2->location().filePath())
1041 else if (n1->location().filePath() > n2->location().filePath())
1044 if (n1->type() < n2->type())
1046 else if (n1->type() > n2->type())
1049 if (n1->name() < n2->name())
1051 else if (n1->name() > n2->name())
1054 if (n1->access() < n2->access())
1056 else if (n1->access() > n2->access())
1059 if (n1->type() == Node::Function && n2->type() == Node::Function) {
1060 const FunctionNode* f1 = static_cast<const FunctionNode*>(n1);
1061 const FunctionNode* f2 = static_cast<const FunctionNode*>(n2);
1063 if (f1->isConst() < f2->isConst())
1065 else if (f1->isConst() > f2->isConst())
1068 if (f1->signature() < f2->signature())
1070 else if (f1->signature() > f2->signature())
1074 if (n1->type() == Node::Document && n2->type() == Node::Document) {
1075 const DocNode* f1 = static_cast<const DocNode*>(n1);
1076 const DocNode* f2 = static_cast<const DocNode*>(n2);
1077 if (f1->fullTitle() < f2->fullTitle())
1079 else if (f1->fullTitle() > f2->fullTitle())
1087 Generate index sections for the child nodes of the given \a node
1088 using the \a writer specified. If \a generateInternalNodes is true,
1089 nodes marked as internal will be included in the index; otherwise,
1090 they will be omitted.
1092 void QDocIndexFiles::generateIndexSections(QXmlStreamWriter& writer,
1094 bool generateInternalNodes)
1097 Note that the groups are written after all the other nodes.
1099 if (!node->isGroup() && generateIndexSection(writer, node, generateInternalNodes)) {
1100 if (node->isInnerNode()) {
1101 const InnerNode* inner = static_cast<const InnerNode*>(node);
1103 NodeList cnodes = inner->childNodes();
1104 qSort(cnodes.begin(), cnodes.end(), compareNodes);
1106 foreach (Node* child, cnodes) {
1108 Don't generate anything for a QML property group node.
1109 It is just a place holder for a collection of QML property
1110 nodes. Recurse to its children, which are the QML property
1113 if (child->subType() == Node::QmlPropertyGroup) {
1114 const InnerNode* pgn = static_cast<const InnerNode*>(child);
1115 foreach (Node* c, pgn->childNodes()) {
1116 generateIndexSections(writer, c, generateInternalNodes);
1120 generateIndexSections(writer, child, generateInternalNodes);
1123 writer.writeEndElement();
1128 Outputs an index file.
1130 void QDocIndexFiles::generateIndex(const QString& fileName,
1132 const QString& title,
1134 bool generateInternalNodes)
1136 QFile file(fileName);
1137 if (!file.open(QFile::WriteOnly | QFile::Text))
1140 QString msg = "Writing index file: " + fileName;
1141 Location::logToStdErr(msg);
1144 QXmlStreamWriter writer(&file);
1145 writer.setAutoFormatting(true);
1146 writer.writeStartDocument();
1147 writer.writeDTD("<!DOCTYPE QDOCINDEX>");
1149 writer.writeStartElement("INDEX");
1150 writer.writeAttribute("url", url);
1151 writer.writeAttribute("title", title);
1152 writer.writeAttribute("version", qdb_->version());
1154 generateIndexSections(writer, qdb_->treeRoot(), generateInternalNodes);
1157 We wait until the end of the index file to output the group elements.
1158 By waiting until the end, when we read each group element, its members
1159 will have already been created. It is then only necessary to create
1160 the group page and add each member to its member list.
1162 const DocNodeMap& groups = qdb_->groups();
1163 if (!groups.isEmpty()) {
1164 DocNodeMap::ConstIterator g = groups.constBegin();
1165 while (g != groups.constEnd()) {
1166 if (generateIndexSection(writer, g.value(), generateInternalNodes))
1167 writer.writeEndElement();
1172 writer.writeEndElement(); // INDEX
1173 writer.writeEndElement(); // QDOCINDEX
1174 writer.writeEndDocument();