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