1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the tools applications of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QMetaObject>
43 #include "codemarker.h"
51 QString CodeMarker::defaultLang;
52 QList<CodeMarker *> CodeMarker::markers;
55 When a code marker constructs itself, it puts itself into
56 the static list of code markers. All the code markers in
57 the static list get initialized in initialize(), which is
58 not called until after the qdoc configuration file has
61 CodeMarker::CodeMarker()
63 markers.prepend(this);
67 When a code marker destroys itself, it removes itself from
68 the static list of code markers.
70 CodeMarker::~CodeMarker()
72 markers.removeAll(this);
76 A code market performs no initialization by default. Marker-specific
77 initialization is performed in subclasses.
79 void CodeMarker::initializeMarker(const Config& ) // config
84 Terminating a code marker is trivial.
86 void CodeMarker::terminateMarker()
92 All the code markers in the static list are initialized
93 here, after the qdoc configuration file has been loaded.
95 void CodeMarker::initialize(const Config& config)
97 defaultLang = config.getString(QLatin1String(CONFIG_LANGUAGE));
98 QList<CodeMarker *>::ConstIterator m = markers.constBegin();
99 while (m != markers.constEnd()) {
100 (*m)->initializeMarker(config);
106 All the code markers in the static list are terminated here.
108 void CodeMarker::terminate()
110 QList<CodeMarker *>::ConstIterator m = markers.constBegin();
111 while (m != markers.constEnd()) {
112 (*m)->terminateMarker();
117 CodeMarker *CodeMarker::markerForCode(const QString& code)
119 CodeMarker *defaultMarker = markerForLanguage(defaultLang);
120 if (defaultMarker != 0 && defaultMarker->recognizeCode(code))
121 return defaultMarker;
123 QList<CodeMarker *>::ConstIterator m = markers.constBegin();
124 while (m != markers.constEnd()) {
125 if ((*m)->recognizeCode(code))
129 return defaultMarker;
132 CodeMarker *CodeMarker::markerForFileName(const QString& fileName)
134 CodeMarker *defaultMarker = markerForLanguage(defaultLang);
136 while ((dot = fileName.lastIndexOf(QLatin1Char('.'), dot)) != -1) {
137 QString ext = fileName.mid(dot + 1);
138 if (defaultMarker != 0 && defaultMarker->recognizeExtension(ext))
139 return defaultMarker;
140 QList<CodeMarker *>::ConstIterator m = markers.constBegin();
141 while (m != markers.constEnd()) {
142 if ((*m)->recognizeExtension(ext))
148 return defaultMarker;
151 CodeMarker *CodeMarker::markerForLanguage(const QString& lang)
153 QList<CodeMarker *>::ConstIterator m = markers.constBegin();
154 while (m != markers.constEnd()) {
155 if ((*m)->recognizeLanguage(lang))
162 const Node *CodeMarker::nodeForString(const QString& string)
164 if (sizeof(const Node *) == sizeof(uint)) {
165 return reinterpret_cast<const Node *>(string.toUInt());
168 return reinterpret_cast<const Node *>(string.toULongLong());
172 QString CodeMarker::stringForNode(const Node *node)
174 if (sizeof(const Node *) == sizeof(ulong)) {
175 return QString::number(reinterpret_cast<quintptr>(node));
178 return QString::number(reinterpret_cast<qulonglong>(node));
182 static const QString samp = QLatin1String("&");
183 static const QString slt = QLatin1String("<");
184 static const QString sgt = QLatin1String(">");
185 static const QString squot = QLatin1String(""");
187 QString CodeMarker::protect(const QString& str)
189 int n = str.length();
191 marked.reserve(n * 2 + 30);
192 const QChar *data = str.constData();
193 for (int i = 0; i != n; ++i) {
194 switch (data[i].unicode()) {
195 case '&': marked += samp; break;
196 case '<': marked += slt; break;
197 case '>': marked += sgt; break;
198 case '"': marked += squot; break;
199 default : marked += data[i];
205 QString CodeMarker::typified(const QString &string)
210 for (int i = 0; i <= string.size(); ++i) {
212 if (i != string.size())
215 QChar lower = ch.toLower();
216 if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z'))
217 || ch.digitValue() >= 0 || ch == QLatin1Char('_')
218 || ch == QLatin1Char(':')) {
222 if (!pendingWord.isEmpty()) {
223 bool isProbablyType = (pendingWord != QLatin1String("const"));
225 result += QLatin1String("<@type>");
226 result += pendingWord;
228 result += QLatin1String("</@type>");
232 switch (ch.unicode()) {
236 result += QLatin1String("&");
239 result += QLatin1String("<");
242 result += QLatin1String(">");
252 QString CodeMarker::taggedNode(const Node* node)
255 QString name = node->name();
257 switch (node->type()) {
258 case Node::Namespace:
259 tag = QLatin1String("@namespace");
262 tag = QLatin1String("@class");
265 tag = QLatin1String("@enum");
268 tag = QLatin1String("@typedef");
271 tag = QLatin1String("@function");
274 tag = QLatin1String("@property");
278 Remove the "QML:" prefix, if present.
279 There shouldn't be any of these "QML:"
280 prefixes in the documentation sources
281 after the switch to using QML module
282 qualifiers, but this code is kept to
283 be backward compatible.
285 if (node->subType() == Node::QmlClass) {
286 if (node->name().startsWith(QLatin1String("QML:")))
289 tag = QLatin1String("@property");
291 case Node::QmlMethod:
292 case Node::QmlSignal:
293 case Node::QmlSignalHandler:
294 tag = QLatin1String("@function");
297 tag = QLatin1String("@unknown");
300 return (QLatin1Char('<') + tag + QLatin1Char('>') + protect(name)
301 + QLatin1String("</") + tag + QLatin1Char('>'));
304 QString CodeMarker::taggedQmlNode(const Node* node)
307 switch (node->type()) {
308 case Node::QmlProperty:
309 tag = QLatin1String("@property");
311 case Node::QmlSignal:
312 tag = QLatin1String("@signal");
314 case Node::QmlSignalHandler:
315 tag = QLatin1String("@signalhandler");
317 case Node::QmlMethod:
318 tag = QLatin1String("@method");
321 tag = QLatin1String("@unknown");
324 return QLatin1Char('<') + tag + QLatin1Char('>') + protect(node->name())
325 + QLatin1String("</") + tag + QLatin1Char('>');
328 QString CodeMarker::linkTag(const Node *node, const QString& body)
330 return QLatin1String("<@link node=\"") + stringForNode(node)
331 + QLatin1String("\">") + body + QLatin1String("</@link>");
334 QString CodeMarker::sortName(const Node *node, const QString* name)
340 nodeName = node->name();
342 for (int i = nodeName.size() - 1; i > 0; --i) {
343 if (nodeName.at(i).digitValue() == -1)
348 // we want 'qint8' to appear before 'qint16'
350 for (int i = 0; i < 4 - numDigits; ++i)
351 nodeName.insert(nodeName.size()-numDigits-1, QLatin1Char('0'));
354 if (node->type() == Node::Function) {
355 const FunctionNode *func = static_cast<const FunctionNode *>(node);
357 if (func->metaness() == FunctionNode::Ctor) {
358 sortNo = QLatin1String("C");
360 else if (func->metaness() == FunctionNode::Dtor) {
361 sortNo = QLatin1String("D");
364 if (nodeName.startsWith(QLatin1String("operator"))
365 && nodeName.length() > 8
366 && !nodeName[8].isLetterOrNumber())
367 sortNo = QLatin1String("F");
369 sortNo = QLatin1String("E");
371 return sortNo + nodeName + QLatin1Char(' ')
372 + QString::number(func->overloadNumber(), 36);
375 if (node->type() == Node::Class)
376 return QLatin1Char('A') + nodeName;
378 if (node->type() == Node::Property || node->type() == Node::Variable)
379 return QLatin1Char('E') + nodeName;
381 if ((node->type() == Node::QmlMethod) ||
382 (node->type() == Node::QmlSignal) ||
383 (node->type() == Node::QmlSignalHandler)) {
384 const FunctionNode* func = static_cast<const FunctionNode *>(node);
385 return QLatin1Char('E') + func->signature();
388 return QLatin1Char('B') + nodeName;
391 void CodeMarker::insert(FastSection &fastSection,
396 bool irrelevant = false;
397 bool inheritedMember = false;
398 if (!node->relates()) {
399 if (node->parent() != (const InnerNode*)fastSection.innerNode && !node->parent()->isAbstract()) {
400 if (node->type() != Node::QmlProperty) {
401 inheritedMember = true;
406 if (node->access() == Node::Private) {
409 else if (node->type() == Node::Function) {
410 FunctionNode *func = (FunctionNode *) node;
411 irrelevant = (inheritedMember
412 && (func->metaness() == FunctionNode::Ctor ||
413 func->metaness() == FunctionNode::Dtor));
415 else if (node->type() == Node::Class || node->type() == Node::Enum
416 || node->type() == Node::Typedef) {
417 irrelevant = (inheritedMember && style != Subpage);
418 if (!irrelevant && style == Detailed && node->type() == Node::Typedef) {
419 const TypedefNode* typedeffe = static_cast<const TypedefNode*>(node);
420 if (typedeffe->associatedEnum())
426 if (status == Compat) {
427 irrelevant = (node->status() != Node::Compat);
429 else if (status == Obsolete) {
430 irrelevant = (node->status() != Node::Obsolete);
433 irrelevant = (node->status() == Node::Compat ||
434 node->status() == Node::Obsolete);
439 if (!inheritedMember || style == Subpage) {
440 QString key = sortName(node);
441 if (!fastSection.memberMap.contains(key))
442 fastSection.memberMap.insert(key, node);
445 if (node->parent()->type() == Node::Class) {
446 if (fastSection.inherited.isEmpty()
447 || fastSection.inherited.last().first != node->parent()) {
448 QPair<InnerNode *, int> p(node->parent(), 0);
449 fastSection.inherited.append(p);
451 fastSection.inherited.last().second++;
457 void CodeMarker::insert(FastSection& fastSection,
460 bool /* includeClassName */)
462 if (node->status() == Node::Compat || node->status() == Node::Obsolete)
465 bool inheritedMember = false;
466 InnerNode* parent = node->parent();
467 if (parent && (parent->type() == Node::Fake) &&
468 (parent->subType() == Node::QmlPropertyGroup)) {
469 parent = parent->parent();
471 inheritedMember = (parent != (const InnerNode*)fastSection.innerNode);
473 if (!inheritedMember || style == Subpage) {
474 QString key = sortName(node);
475 if (!fastSection.memberMap.contains(key))
476 fastSection.memberMap.insert(key, node);
479 if ((parent->type() == Node::Fake) && (parent->subType() == Node::QmlClass)) {
480 if (fastSection.inherited.isEmpty()
481 || fastSection.inherited.last().first != parent) {
482 QPair<InnerNode*, int> p(parent, 0);
483 fastSection.inherited.append(p);
485 fastSection.inherited.last().second++;
491 Returns true if \a node represents a reimplemented member function.
492 If it is, then it is inserted in the reimplemented member map in the
493 section \a fs. And, the test is only performed if \a status is \e OK.
494 Otherwise, false is returned.
496 bool CodeMarker::insertReimpFunc(FastSection& fs, Node* node, Status status)
498 if (node->access() == Node::Private)
501 const FunctionNode* fn = static_cast<const FunctionNode*>(node);
502 if ((fn->reimplementedFrom() != 0) && (status == Okay)) {
503 bool inherited = (!fn->relates() && (fn->parent() != (const InnerNode*)fs.innerNode));
505 QString key = sortName(fn);
506 if (!fs.reimpMemberMap.contains(key)) {
507 fs.reimpMemberMap.insert(key,node);
516 If \a fs is not empty, convert it to a Section and append
517 the new Section to \a sectionList.
519 void CodeMarker::append(QList<Section>& sectionList, const FastSection& fs, bool includeKeys)
522 Section section(fs.name,fs.divClass,fs.singularMember,fs.pluralMember);
524 section.keys = fs.memberMap.keys();
526 section.members = fs.memberMap.values();
527 section.reimpMembers = fs.reimpMemberMap.values();
528 section.inherited = fs.inherited;
529 sectionList.append(section);
533 static QString encode(const QString &string)
538 QStringList CodeMarker::macRefsForNode(Node *node)
540 QString result = QLatin1String("cpp/");
541 switch (node->type()) {
544 const ClassNode *classe = static_cast<const ClassNode *>(node);
546 result += QLatin1String("cl/");
548 result += macName(classe); // ### Maybe plainName?
553 QStringList stringList;
554 stringList << encode(result + QLatin1String("tag/") +
556 foreach (const QString &enumName, node->doc().enumItemNames()) {
557 // ### Write a plainEnumValue() and use it here
558 stringList << encode(result + QLatin1String("econst/") +
559 macName(node->parent(), enumName));
564 result += QLatin1String("tdef/") + macName(node);
568 bool isMacro = false;
570 const FunctionNode *func = static_cast<const FunctionNode *>(node);
572 // overloads are too clever for the Xcode documentation browser
573 if (func->isOverload())
574 return QStringList();
576 if (func->metaness() == FunctionNode::MacroWithParams
577 || func->metaness() == FunctionNode::MacroWithoutParams) {
578 result += QLatin1String("macro/");
580 else if (func->isStatic()) {
581 result += QLatin1String("clm/");
583 else if (!func->parent()->name().isEmpty()) {
584 result += QLatin1String("instm/");
587 result += QLatin1String("func/");
590 result += macName(func);
591 if (result.endsWith(QLatin1String("()")))
596 result += QLatin1String("data/") + macName(node);
600 NodeList list = static_cast<const PropertyNode*>(node)->functions();
601 QStringList stringList;
602 foreach (Node* node, list) {
603 stringList += macRefsForNode(node);
607 case Node::Namespace:
610 return QStringList();
613 return QStringList(encode(result));
616 QString CodeMarker::macName(const Node *node, const QString &name)
618 QString myName = name;
619 if (myName.isEmpty()) {
620 myName = node->name();
621 node = node->parent();
624 if (node->name().isEmpty()) {
625 return QLatin1Char('/') + protect(myName);
628 return plainFullName(node) + QLatin1Char('/') + protect(myName);
633 Get the list of documentation sections for the children of
634 the specified QmlClassNode.
636 QList<Section> CodeMarker::qmlSections(const QmlClassNode* ,
639 return QList<Section>();
642 const Node* CodeMarker::resolveTarget(const QString& /* target */,