1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt for Python.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include "doxygenparser.h"
30 #include "abstractmetalang.h"
31 #include "messages.h"
32 #include "propertyspec.h"
33 #include "reporthandler.h"
34 #include "typesystem.h"
35 #include "xmlutils.h"
36
37 #include <QtCore/QFile>
38 #include <QtCore/QDir>
39
getSectionKindAttr(const AbstractMetaFunction * func)40 static QString getSectionKindAttr(const AbstractMetaFunction *func)
41 {
42 if (func->isSignal())
43 return QLatin1String("signal");
44 QString kind = func->isPublic()
45 ? QLatin1String("public") : QLatin1String("protected");
46 if (func->isStatic())
47 kind += QLatin1String("-static");
48 else if (func->isSlot())
49 kind += QLatin1String("-slot");
50 return kind;
51 }
52
retrieveModuleDocumentation()53 Documentation DoxygenParser::retrieveModuleDocumentation()
54 {
55 return retrieveModuleDocumentation(packageName());
56 }
57
fillDocumentation(AbstractMetaClass * metaClass)58 void DoxygenParser::fillDocumentation(AbstractMetaClass* metaClass)
59 {
60 if (!metaClass)
61 return;
62
63 QString doxyFileSuffix;
64 if (metaClass->enclosingClass()) {
65 doxyFileSuffix += metaClass->enclosingClass()->name();
66 doxyFileSuffix += QLatin1String("_1_1"); // FIXME: Check why _1_1!!
67 }
68 doxyFileSuffix += metaClass->name();
69 doxyFileSuffix += QLatin1String(".xml");
70
71 const char* prefixes[] = { "class", "struct", "namespace" };
72 bool isProperty = false;
73
74 QString doxyFilePath;
75 for (const char *prefix : prefixes) {
76 doxyFilePath = documentationDataDirectory() + QLatin1Char('/')
77 + QLatin1String(prefix) + doxyFileSuffix;
78 if (QFile::exists(doxyFilePath))
79 break;
80 doxyFilePath.clear();
81 }
82
83 if (doxyFilePath.isEmpty()) {
84 qCWarning(lcShibokenDoc).noquote().nospace()
85 << "Can't find doxygen file for class " << metaClass->name() << ", tried: "
86 << QDir::toNativeSeparators(documentationDataDirectory())
87 << "/{struct|class|namespace}"<< doxyFileSuffix;
88 return;
89 }
90
91 QString errorMessage;
92 XQueryPtr xquery = XQuery::create(doxyFilePath, &errorMessage);
93 if (xquery.isNull()) {
94 qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
95 return;
96 }
97
98 static const QList<QPair<Documentation::Type, QString>> docTags = {
99 { Documentation::Brief, QLatin1String("briefdescription") },
100 { Documentation::Detailed, QLatin1String("detaileddescription") }
101 };
102 // Get class documentation
103 Documentation classDoc;
104
105 for (const auto &tag : docTags) {
106 const QString classQuery = QLatin1String("/doxygen/compounddef/") + tag.second;
107 QString doc = getDocumentation(xquery, classQuery,
108 metaClass->typeEntry()->docModifications());
109 if (doc.isEmpty())
110 qCWarning(lcShibokenDoc, "%s",
111 qPrintable(msgCannotFindDocumentation(doxyFilePath, "class", metaClass->name(),
112 classQuery)));
113 else
114 classDoc.setValue(doc, tag.first);
115 }
116 metaClass->setDocumentation(classDoc);
117
118 //Functions Documentation
119 const AbstractMetaFunctionList &funcs = DocParser::documentableFunctions(metaClass);
120 for (AbstractMetaFunction *func : funcs) {
121 QString query = QLatin1String("/doxygen/compounddef/sectiondef");
122 // properties
123 if (func->isPropertyReader() || func->isPropertyWriter()
124 || func->isPropertyResetter()) {
125 query += QLatin1String("[@kind=\"property\"]/memberdef/name[text()=\"")
126 + func->propertySpec()->name() + QLatin1String("\"]");
127 isProperty = true;
128 } else { // normal methods
129 QString kind = getSectionKindAttr(func);
130 query += QLatin1String("[@kind=\"") + kind
131 + QLatin1String("-func\"]/memberdef/name[text()=\"")
132 + func->originalName() + QLatin1String("\"]");
133
134 if (func->arguments().isEmpty()) {
135 QString args = func->isConstant() ? QLatin1String("() const ") : QLatin1String("()");
136 query += QLatin1String("/../argsstring[text()=\"") + args + QLatin1String("\"]");
137 } else {
138 int i = 1;
139 const AbstractMetaArgumentList &arguments = func->arguments();
140 for (AbstractMetaArgument *arg : arguments) {
141 if (!arg->type()->isPrimitive()) {
142 query += QLatin1String("/../param[") + QString::number(i)
143 + QLatin1String("]/type/ref[text()=\"")
144 + arg->type()->cppSignature().toHtmlEscaped()
145 + QLatin1String("\"]/../..");
146 } else {
147 query += QLatin1String("/../param[") + QString::number(i)
148 + QLatin1String("]/type[text(), \"")
149 + arg->type()->cppSignature().toHtmlEscaped()
150 + QLatin1String("\"]/..");
151 }
152 ++i;
153 }
154 }
155 }
156 Documentation funcDoc;
157 for (const auto &tag : docTags) {
158 QString funcQuery(query);
159 if (!isProperty) {
160 funcQuery += QLatin1String("/../") + tag.second;
161 } else {
162 funcQuery = QLatin1Char('(') + funcQuery;
163 funcQuery += QLatin1String("/../%1)[1]").arg(tag.second);
164 }
165
166 QString doc = getDocumentation(xquery, funcQuery, DocModificationList());
167 if (doc.isEmpty()) {
168 qCWarning(lcShibokenDoc, "%s",
169 qPrintable(msgCannotFindDocumentation(doxyFilePath, metaClass, func,
170 funcQuery)));
171 } else {
172 funcDoc.setValue(doc, tag.first);
173 }
174 }
175 func->setDocumentation(funcDoc);
176 isProperty = false;
177 }
178
179 //Fields
180 const AbstractMetaFieldList &fields = metaClass->fields();
181 for (AbstractMetaField *field : fields) {
182 if (field->isPrivate())
183 return;
184
185 Documentation fieldDoc;
186 for (const auto &tag : docTags) {
187 QString query = QLatin1String("/doxygen/compounddef/sectiondef/memberdef/name[text()=\"")
188 + field->name() + QLatin1String("\"]/../") + tag.second;
189 QString doc = getDocumentation(xquery, query, DocModificationList());
190 if (doc.isEmpty()) {
191 qCWarning(lcShibokenDoc, "%s",
192 qPrintable(msgCannotFindDocumentation(doxyFilePath, metaClass, field,
193 query)));
194 } else {
195 fieldDoc.setValue(doc, tag.first);
196 }
197 }
198 field->setDocumentation(fieldDoc);
199 }
200
201 //Enums
202 for (AbstractMetaEnum *meta_enum : metaClass->enums()) {
203 QString query = QLatin1String("/doxygen/compounddef/sectiondef/memberdef[@kind=\"enum\"]/name[text()=\"")
204 + meta_enum->name() + QLatin1String("\"]/..");
205 QString doc = getDocumentation(xquery, query, DocModificationList());
206 if (doc.isEmpty()) {
207 qCWarning(lcShibokenDoc, "%s",
208 qPrintable(msgCannotFindDocumentation(doxyFilePath, metaClass, meta_enum, query)));
209 }
210 meta_enum->setDocumentation(doc);
211 }
212
213 }
214
retrieveModuleDocumentation(const QString & name)215 Documentation DoxygenParser::retrieveModuleDocumentation(const QString& name){
216
217 QString sourceFile = documentationDataDirectory() + QLatin1String("/indexpage.xml");
218
219 if (!QFile::exists(sourceFile)) {
220 qCWarning(lcShibokenDoc).noquote().nospace()
221 << "Can't find doxygen XML file for module " << name << ", tried: "
222 << QDir::toNativeSeparators(sourceFile);
223 return Documentation();
224 }
225
226 QString errorMessage;
227 XQueryPtr xquery = XQuery::create(sourceFile, &errorMessage);
228 if (xquery.isNull()) {
229 qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
230 return {};
231 }
232
233 // Module documentation
234 QString query = QLatin1String("/doxygen/compounddef/detaileddescription");
235 return Documentation(getDocumentation(xquery, query, DocModificationList()));
236 }
237
238