source: trunk/tools/qdoc3/codemarker.cpp@ 1039

Last change on this file since 1039 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 19.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QMetaObject>
43#include <QDebug>
44#include "codemarker.h"
45#include "config.h"
46#include "node.h"
47
48#include <stdio.h>
49
50QT_BEGIN_NAMESPACE
51
52QString CodeMarker::defaultLang;
53QList<CodeMarker *> CodeMarker::markers;
54
55/*!
56 When a code marker constructs itself, it puts itself into
57 the static list of code markers. All the code markers in
58 the static list get initialized in initialize(), which is
59 not called until after the qdoc configuration file has
60 been read.
61 */
62CodeMarker::CodeMarker()
63 : slow(false)
64{
65 markers.prepend(this);
66}
67
68/*!
69 When a code marker destroys itself, it removes itself from
70 the static list of code markers.
71 */
72CodeMarker::~CodeMarker()
73{
74 markers.removeAll(this);
75}
76
77/*!
78 The only thing a code market initializes is its \e{slow}
79 flag. The \e{slow} flag indicates whether the operations
80 that slow down qdoc are to be performed or not. It is
81 turned off by default.
82 */
83void CodeMarker::initializeMarker(const Config &config)
84{
85 slow = config.getBool(QLatin1String(CONFIG_SLOW));
86}
87
88/*!
89 Terminating a code marker is trivial.
90 */
91void CodeMarker::terminateMarker()
92{
93 // nothing.
94}
95
96/*!
97 All the code markers in the static list are initialized
98 here, after the qdoc configuration file has been loaded.
99 */
100void CodeMarker::initialize(const Config& config)
101{
102 defaultLang = config.getString(QLatin1String(CONFIG_LANGUAGE));
103 QList<CodeMarker *>::ConstIterator m = markers.begin();
104 while (m != markers.end()) {
105 (*m)->initializeMarker(config);
106 ++m;
107 }
108}
109
110/*!
111 All the code markers in the static list are terminated here.
112 */
113void CodeMarker::terminate()
114{
115 QList<CodeMarker *>::ConstIterator m = markers.begin();
116 while (m != markers.end()) {
117 (*m)->terminateMarker();
118 ++m;
119 }
120}
121
122CodeMarker *CodeMarker::markerForCode(const QString& code)
123{
124 CodeMarker *defaultMarker = markerForLanguage(defaultLang);
125 if (defaultMarker != 0 && defaultMarker->recognizeCode(code))
126 return defaultMarker;
127
128 QList<CodeMarker *>::ConstIterator m = markers.begin();
129 while (m != markers.end()) {
130 if ((*m)->recognizeCode(code))
131 return *m;
132 ++m;
133 }
134 return defaultMarker;
135}
136
137CodeMarker *CodeMarker::markerForFileName(const QString& fileName)
138{
139 CodeMarker *defaultMarker = markerForLanguage(defaultLang);
140 int dot = -1;
141 while ((dot = fileName.lastIndexOf(QLatin1Char('.'), dot)) != -1) {
142 QString ext = fileName.mid(dot + 1);
143 if (defaultMarker != 0 && defaultMarker->recognizeExtension(ext))
144 return defaultMarker;
145 QList<CodeMarker *>::ConstIterator m = markers.begin();
146 while (m != markers.end()) {
147 if ((*m)->recognizeExtension(ext))
148 return *m;
149 ++m;
150 }
151 --dot;
152 }
153 return defaultMarker;
154}
155
156CodeMarker *CodeMarker::markerForLanguage(const QString& lang)
157{
158 QList<CodeMarker *>::ConstIterator m = markers.begin();
159 while (m != markers.end()) {
160 if ((*m)->recognizeLanguage(lang))
161 return *m;
162 ++m;
163 }
164 return 0;
165}
166
167const Node *CodeMarker::nodeForString(const QString& string)
168{
169 if (sizeof(const Node *) == sizeof(uint)) {
170 return reinterpret_cast<const Node *>(string.toUInt());
171 }
172 else {
173 return reinterpret_cast<const Node *>(string.toULongLong());
174 }
175}
176
177QString CodeMarker::stringForNode(const Node *node)
178{
179 if (sizeof(const Node *) == sizeof(ulong)) {
180 return QString::number(reinterpret_cast<quintptr>(node));
181 }
182 else {
183 return QString::number(reinterpret_cast<qulonglong>(node));
184 }
185}
186
187static const QString samp = QLatin1String("&amp;");
188static const QString slt = QLatin1String("&lt;");
189static const QString sgt = QLatin1String("&gt;");
190static const QString squot = QLatin1String("&quot;");
191
192QString CodeMarker::protect(const QString& str)
193{
194 int n = str.length();
195 QString marked;
196 marked.reserve(n * 2 + 30);
197 const QChar *data = str.constData();
198 for (int i = 0; i != n; ++i) {
199 switch (data[i].unicode()) {
200 case '&': marked += samp; break;
201 case '<': marked += slt; break;
202 case '>': marked += sgt; break;
203 case '"': marked += squot; break;
204 default : marked += data[i];
205 }
206 }
207 return marked;
208}
209
210QString CodeMarker::typified(const QString &string)
211{
212 QString result;
213 QString pendingWord;
214
215 for (int i = 0; i <= string.size(); ++i) {
216 QChar ch;
217 if (i != string.size())
218 ch = string.at(i);
219
220 QChar lower = ch.toLower();
221 if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z'))
222 || ch.digitValue() >= 0 || ch == QLatin1Char('_')
223 || ch == QLatin1Char(':')) {
224 pendingWord += ch;
225 }
226 else {
227 if (!pendingWord.isEmpty()) {
228 bool isProbablyType = (pendingWord != QLatin1String("const"));
229 if (isProbablyType)
230 result += QLatin1String("<@type>");
231 result += pendingWord;
232 if (isProbablyType)
233 result += QLatin1String("</@type>");
234 }
235 pendingWord.clear();
236
237 switch (ch.unicode()) {
238 case '\0':
239 break;
240 case '&':
241 result += QLatin1String("&amp;");
242 break;
243 case '<':
244 result += QLatin1String("&lt;");
245 break;
246 case '>':
247 result += QLatin1String("&gt;");
248 break;
249 default:
250 result += ch;
251 }
252 }
253 }
254 return result;
255}
256
257QString CodeMarker::taggedNode(const Node* node)
258{
259 QString tag;
260 QString name = node->name();
261
262 switch (node->type()) {
263 case Node::Namespace:
264 tag = QLatin1String("@namespace");
265 break;
266 case Node::Class:
267 tag = QLatin1String("@class");
268 break;
269 case Node::Enum:
270 tag = QLatin1String("@enum");
271 break;
272 case Node::Typedef:
273 tag = QLatin1String("@typedef");
274 break;
275 case Node::Function:
276 tag = QLatin1String("@function");
277 break;
278 case Node::Property:
279 tag = QLatin1String("@property");
280 break;
281#ifdef QDOC_QML
282 case Node::Fake:
283 if (node->subType() == Node::QmlClass) {
284 if (node->name().startsWith(QLatin1String("QML:")))
285 name = name.mid(4); // remove the "QML:" prefix
286 }
287 tag = QLatin1String("@property");
288 break;
289#endif
290 default:
291 tag = QLatin1String("@unknown");
292 break;
293 }
294 return QLatin1Char('<') + tag + QLatin1Char('>') + protect(name)
295 + QLatin1String("</") + tag + QLatin1Char('>');
296}
297
298#ifdef QDOC_QML
299QString CodeMarker::taggedQmlNode(const Node* node)
300{
301 QString tag;
302 switch (node->type()) {
303 case Node::QmlProperty:
304 tag = QLatin1String("@property");
305 break;
306 case Node::QmlSignal:
307 tag = QLatin1String("@signal");
308 break;
309 case Node::QmlMethod:
310 tag = QLatin1String("@method");
311 break;
312 default:
313 tag = QLatin1String("@unknown");
314 break;
315 }
316 return QLatin1Char('<') + tag + QLatin1Char('>') + protect(node->name())
317 + QLatin1String("</") + tag + QLatin1Char('>');
318}
319#endif
320
321QString CodeMarker::linkTag(const Node *node, const QString& body)
322{
323 return QLatin1String("<@link node=\"") + stringForNode(node)
324 + QLatin1String("\">") + body + QLatin1String("</@link>");
325}
326
327QString CodeMarker::sortName(const Node *node)
328{
329 QString nodeName = node->name();
330 int numDigits = 0;
331 for (int i = nodeName.size() - 1; i > 0; --i) {
332 if (nodeName.at(i).digitValue() == -1)
333 break;
334 ++numDigits;
335 }
336
337 // we want 'qint8' to appear before 'qint16'
338 if (numDigits > 0) {
339 for (int i = 0; i < 4 - numDigits; ++i)
340 nodeName.insert(nodeName.size()-numDigits-1, QLatin1String("0"));
341 }
342
343 if (node->type() == Node::Function) {
344 const FunctionNode *func = static_cast<const FunctionNode *>(node);
345 QString sortNo;
346 if (func->metaness() == FunctionNode::Ctor) {
347 sortNo = QLatin1String("C");
348 }
349 else if (func->metaness() == FunctionNode::Dtor) {
350 sortNo = QLatin1String("D");
351 }
352 else {
353 if (nodeName.startsWith(QLatin1String("operator"))
354 && nodeName.length() > 8
355 && !nodeName[8].isLetterOrNumber())
356 sortNo = QLatin1String("F");
357 else
358 sortNo = QLatin1String("E");
359 }
360 return sortNo + nodeName + QLatin1Char(' ')
361 + QString::number(func->overloadNumber(), 36);
362 }
363
364 if (node->type() == Node::Class)
365 return QLatin1Char('A') + nodeName;
366
367 if (node->type() == Node::Property || node->type() == Node::Variable)
368 return QLatin1Char('E') + nodeName;
369
370 return QLatin1Char('B') + nodeName;
371}
372
373void CodeMarker::insert(FastSection &fastSection,
374 Node *node,
375 SynopsisStyle style,
376 Status status)
377{
378 bool irrelevant = false;
379 bool inheritedMember = false;
380 if (!node->relates()) {
381 if (node->parent() != (const InnerNode*)fastSection.innerNode) {
382 if (node->type() != Node::QmlProperty)
383 inheritedMember = true;
384 }
385 }
386
387 if (node->access() == Node::Private) {
388 irrelevant = true;
389 }
390 else if (node->type() == Node::Function) {
391 FunctionNode *func = (FunctionNode *) node;
392 irrelevant = (inheritedMember
393 && (func->metaness() == FunctionNode::Ctor ||
394 func->metaness() == FunctionNode::Dtor));
395 }
396 else if (node->type() == Node::Class || node->type() == Node::Enum
397 || node->type() == Node::Typedef) {
398 irrelevant = (inheritedMember && style != SeparateList);
399 if (!irrelevant && style == Detailed && node->type() == Node::Typedef) {
400 const TypedefNode* typedeffe = static_cast<const TypedefNode*>(node);
401 if (typedeffe->associatedEnum())
402 irrelevant = true;
403 }
404 }
405
406 if (!irrelevant) {
407 if (status == Compat) {
408 irrelevant = (node->status() != Node::Compat);
409 }
410 else if (status == Obsolete) {
411 irrelevant = (node->status() != Node::Obsolete);
412 }
413 else {
414 irrelevant = (node->status() == Node::Compat ||
415 node->status() == Node::Obsolete);
416 }
417 }
418
419 if (!irrelevant) {
420 if (!inheritedMember || style == SeparateList) {
421 QString key = sortName(node);
422 if (!fastSection.memberMap.contains(key))
423 fastSection.memberMap.insert(key, node);
424 }
425 else {
426 if (node->parent()->type() == Node::Class) {
427 if (fastSection.inherited.isEmpty()
428 || fastSection.inherited.last().first != node->parent()) {
429 QPair<ClassNode *, int> p((ClassNode *)node->parent(), 0);
430 fastSection.inherited.append(p);
431 }
432 fastSection.inherited.last().second++;
433 }
434 }
435 }
436}
437
438/*!
439 Returns true if \a node represents a reimplemented member function.
440 If it is, then it is inserted in the reimplemented member map in the
441 section \a fs. And, the test is only performed if \a status is \e OK.
442 Otherwise, false is returned.
443 */
444bool CodeMarker::insertReimpFunc(FastSection& fs, Node* node, Status status)
445{
446 if (node->access() == Node::Private)
447 return false;
448
449 const FunctionNode* fn = static_cast<const FunctionNode*>(node);
450 if ((fn->reimplementedFrom() != 0) && (status == Okay)) {
451 bool inherited = (!fn->relates() && (fn->parent() != (const InnerNode*)fs.innerNode));
452 if (!inherited) {
453 QString key = sortName(fn);
454 if (!fs.reimpMemberMap.contains(key)) {
455 fs.reimpMemberMap.insert(key,node);
456 return true;
457 }
458 }
459 }
460 return false;
461 }
462
463/*!
464 If \a fs is not empty, convert it to a Section and append
465 the new Section to \a sectionList.
466 */
467void CodeMarker::append(QList<Section>& sectionList, const FastSection& fs)
468{
469 if (!fs.isEmpty()) {
470 Section section(fs.name,fs.divClass,fs.singularMember,fs.pluralMember);
471 section.members = fs.memberMap.values();
472 section.reimpMembers = fs.reimpMemberMap.values();
473 section.inherited = fs.inherited;
474 sectionList.append(section);
475 }
476}
477
478static QString encode(const QString &string)
479{
480#if 0
481 QString result = string;
482
483 for (int i = string.size() - 1; i >= 0; --i) {
484 uint ch = string.at(i).unicode();
485 if (ch > 0xFF)
486 ch = '?';
487 if ((ch - '0') >= 10 && (ch - 'a') >= 26 && (ch - 'A') >= 26
488 && ch != '/' && ch != '(' && ch != ')' && ch != ',' && ch != '*'
489 && ch != '&' && ch != '_' && ch != '<' && ch != '>' && ch != ':'
490 && ch != '~')
491 result.replace(i, 1, QString("%") + QString("%1").arg(ch, 2, 16));
492 }
493 return result;
494#else
495 return string;
496#endif
497}
498
499QStringList CodeMarker::macRefsForNode(const Node *node)
500{
501 QString result = QLatin1String("cpp/");
502 switch (node->type()) {
503 case Node::Class:
504 {
505 const ClassNode *classe = static_cast<const ClassNode *>(node);
506#if 0
507 if (!classe->templateStuff().isEmpty()) {
508 result += QLatin1String("tmplt/");
509 }
510 else
511#endif
512 {
513 result += QLatin1String("cl/");
514 }
515 result += macName(classe); // ### Maybe plainName?
516 }
517 break;
518 case Node::Enum:
519 {
520 QStringList stringList;
521 stringList << encode(result + QLatin1String("tag/") +
522 macName(node));
523 foreach (const QString &enumName, node->doc().enumItemNames()) {
524 // ### Write a plainEnumValue() and use it here
525 stringList << encode(result + QLatin1String("econst/") +
526 macName(node->parent(), enumName));
527 }
528 return stringList;
529 }
530 case Node::Typedef:
531 result += QLatin1String("tdef/") + macName(node);
532 break;
533 case Node::Function:
534 {
535 bool isMacro = false;
536 const FunctionNode *func = static_cast<const FunctionNode *>(node);
537
538 // overloads are too clever for the Xcode documentation browser
539 if (func->isOverload())
540 return QStringList();
541
542 if (func->metaness() == FunctionNode::MacroWithParams
543 || func->metaness() == FunctionNode::MacroWithoutParams) {
544 result += QLatin1String("macro/");
545 isMacro = true;
546#if 0
547 }
548 else if (!func->templateStuff().isEmpty()) {
549 result += QLatin1String("ftmplt/");
550#endif
551 }
552 else if (func->isStatic()) {
553 result += QLatin1String("clm/");
554 }
555 else if (!func->parent()->name().isEmpty()) {
556 result += QLatin1String("instm/");
557 }
558 else {
559 result += QLatin1String("func/");
560 }
561
562 result += macName(func);
563 if (result.endsWith(QLatin1String("()")))
564 result.chop(2);
565#if 0
566 // this code is too clever for the Xcode documentation
567 // browser and/or pbhelpindexer
568 if (!isMacro) {
569 result += "/" + QLatin1String(QMetaObject::normalizedSignature(func->returnType().toLatin1().constData())) + "/(";
570 const QList<Parameter> &params = func->parameters();
571 for (int i = 0; i < params.count(); ++i) {
572 QString type = params.at(i).leftType() +
573 params.at(i).rightType();
574 type = QLatin1String(QMetaObject::normalizedSignature(type.toLatin1().constData()));
575 if (i != 0)
576 result += ",";
577 result += type;
578 }
579 result += ")";
580 }
581#endif
582 }
583 break;
584 case Node::Variable:
585 result += QLatin1String("data/") + macName(node);
586 break;
587 case Node::Property:
588 {
589 NodeList list = static_cast<const PropertyNode*>(node)->functions();
590 QStringList stringList;
591 foreach (const Node *node, list) {
592 stringList += macRefsForNode(node);
593 }
594 return stringList;
595 }
596 case Node::Namespace:
597 case Node::Fake:
598 case Node::Target:
599 default:
600 return QStringList();
601 }
602
603 return QStringList(encode(result));
604}
605
606QString CodeMarker::macName(const Node *node, const QString &name)
607{
608 QString myName = name;
609 if (myName.isEmpty()) {
610 myName = node->name();
611 node = node->parent();
612 }
613
614 if (node->name().isEmpty()) {
615 return QLatin1Char('/') + protect(myName);
616 }
617 else {
618 return plainFullName(node) + QLatin1Char('/') + protect(myName);
619 }
620}
621
622#ifdef QDOC_QML
623/*!
624 Get the list of documentation sections for the children of
625 the specified QmlClassNode.
626 */
627QList<Section> CodeMarker::qmlSections(const QmlClassNode* ,
628 SynopsisStyle ,
629 const Tree* )
630{
631 return QList<Section>();
632}
633#endif
634
635const Node* CodeMarker::resolveTarget(const QString& ,
636 const Tree* ,
637 const Node* ,
638 const Node* )
639{
640 return 0;
641}
642
643QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.