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

Last change on this file since 779 was 651, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 18.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 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<ulong>(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
261 switch (node->type()) {
262 case Node::Namespace:
263 tag = QLatin1String("@namespace");
264 break;
265 case Node::Class:
266 tag = QLatin1String("@class");
267 break;
268 case Node::Enum:
269 tag = QLatin1String("@enum");
270 break;
271 case Node::Typedef:
272 tag = QLatin1String("@typedef");
273 break;
274 case Node::Function:
275 tag = QLatin1String("@function");
276 break;
277 case Node::Property:
278 tag = QLatin1String("@property");
279 break;
280 default:
281 tag = QLatin1String("@unknown");
282 break;
283 }
284 return QLatin1Char('<') + tag + QLatin1Char('>') + protect(node->name())
285 + QLatin1String("</") + tag + QLatin1Char('>');
286}
287
288#ifdef QDOC_QML
289QString CodeMarker::taggedQmlNode(const Node* node)
290{
291 QString tag;
292 switch (node->type()) {
293 case Node::QmlProperty:
294 tag = QLatin1String("@property");
295 break;
296 case Node::QmlSignal:
297 tag = QLatin1String("@signal");
298 break;
299 case Node::QmlMethod:
300 tag = QLatin1String("@method");
301 break;
302 default:
303 tag = QLatin1String("@unknown");
304 break;
305 }
306 return QLatin1Char('<') + tag + QLatin1Char('>') + protect(node->name())
307 + QLatin1String("</") + tag + QLatin1Char('>');
308}
309#endif
310
311QString CodeMarker::linkTag(const Node *node, const QString& body)
312{
313 return QLatin1String("<@link node=\"") + stringForNode(node)
314 + QLatin1String("\">") + body + QLatin1String("</@link>");
315}
316
317QString CodeMarker::sortName(const Node *node)
318{
319 QString nodeName = node->name();
320 int numDigits = 0;
321 for (int i = nodeName.size() - 1; i > 0; --i) {
322 if (nodeName.at(i).digitValue() == -1)
323 break;
324 ++numDigits;
325 }
326
327 // we want 'qint8' to appear before 'qint16'
328 if (numDigits > 0) {
329 for (int i = 0; i < 4 - numDigits; ++i)
330 nodeName.insert(nodeName.size()-numDigits-1, QLatin1String("0"));
331 }
332
333 if (node->type() == Node::Function) {
334 const FunctionNode *func = static_cast<const FunctionNode *>(node);
335 QString sortNo;
336 if (func->metaness() == FunctionNode::Ctor) {
337 sortNo = QLatin1String("C");
338 }
339 else if (func->metaness() == FunctionNode::Dtor) {
340 sortNo = QLatin1String("D");
341 }
342 else {
343 if (nodeName.startsWith(QLatin1String("operator"))
344 && nodeName.length() > 8
345 && !nodeName[8].isLetterOrNumber())
346 sortNo = QLatin1String("F");
347 else
348 sortNo = QLatin1String("E");
349 }
350 return sortNo + nodeName + QLatin1Char(' ')
351 + QString::number(func->overloadNumber(), 36);
352 }
353
354 if (node->type() == Node::Class)
355 return QLatin1Char('A') + nodeName;
356
357 if (node->type() == Node::Property || node->type() == Node::Variable)
358 return QLatin1Char('E') + nodeName;
359
360 return QLatin1Char('B') + nodeName;
361}
362
363void CodeMarker::insert(FastSection &fastSection,
364 Node *node,
365 SynopsisStyle style,
366 Status status)
367{
368 bool irrelevant = false;
369 bool inheritedMember = false;
370 if (!node->relates()) {
371 if (node->parent() != (const InnerNode*)fastSection.innerNode) {
372 if (node->type() != Node::QmlProperty)
373 inheritedMember = true;
374 }
375 }
376
377 if (node->access() == Node::Private) {
378 irrelevant = true;
379 }
380 else if (node->type() == Node::Function) {
381 FunctionNode *func = (FunctionNode *) node;
382 irrelevant = (inheritedMember
383 && (func->metaness() == FunctionNode::Ctor ||
384 func->metaness() == FunctionNode::Dtor));
385 }
386 else if (node->type() == Node::Class || node->type() == Node::Enum
387 || node->type() == Node::Typedef) {
388 irrelevant = (inheritedMember && style != SeparateList);
389 if (!irrelevant && style == Detailed && node->type() == Node::Typedef) {
390 const TypedefNode* typedeffe = static_cast<const TypedefNode*>(node);
391 if (typedeffe->associatedEnum())
392 irrelevant = true;
393 }
394 }
395
396 if (!irrelevant) {
397 if (status == Compat) {
398 irrelevant = (node->status() != Node::Compat);
399 }
400 else if (status == Obsolete) {
401 irrelevant = (node->status() != Node::Obsolete);
402 }
403 else {
404 irrelevant = (node->status() == Node::Compat ||
405 node->status() == Node::Obsolete);
406 }
407 }
408
409 if (!irrelevant) {
410 if (!inheritedMember || style == SeparateList) {
411 QString key = sortName(node);
412 if (!fastSection.memberMap.contains(key))
413 fastSection.memberMap.insert(key, node);
414 }
415 else {
416 if (node->parent()->type() == Node::Class) {
417 if (fastSection.inherited.isEmpty()
418 || fastSection.inherited.last().first != node->parent()) {
419 QPair<ClassNode *, int> p((ClassNode *)node->parent(), 0);
420 fastSection.inherited.append(p);
421 }
422 fastSection.inherited.last().second++;
423 }
424 }
425 }
426}
427
428/*!
429 Returns true if \a node represents a reimplemented member function.
430 If it is, then it is inserted in the reimplemented member map in the
431 section \a fs. And, the test is only performed if \a status is \e OK.
432 Otherwise, false is returned.
433 */
434bool CodeMarker::insertReimpFunc(FastSection& fs, Node* node, Status status)
435{
436 if (node->access() == Node::Private)
437 return false;
438
439 const FunctionNode* fn = static_cast<const FunctionNode*>(node);
440 if ((fn->reimplementedFrom() != 0) && (status == Okay)) {
441 bool inherited = (!fn->relates() && (fn->parent() != (const InnerNode*)fs.innerNode));
442 if (!inherited) {
443 QString key = sortName(fn);
444 if (!fs.reimpMemberMap.contains(key)) {
445 fs.reimpMemberMap.insert(key,node);
446 return true;
447 }
448 }
449 }
450 return false;
451 }
452
453/*!
454 If \a fs is not empty, convert it to a Section and append
455 the new Section to \a sectionList.
456 */
457void CodeMarker::append(QList<Section>& sectionList, const FastSection& fs)
458{
459 if (!fs.isEmpty()) {
460 Section section(fs.name,fs.singularMember,fs.pluralMember);
461 section.members = fs.memberMap.values();
462 section.reimpMembers = fs.reimpMemberMap.values();
463 section.inherited = fs.inherited;
464 sectionList.append(section);
465 }
466}
467
468static QString encode(const QString &string)
469{
470#if 0
471 QString result = string;
472
473 for (int i = string.size() - 1; i >= 0; --i) {
474 uint ch = string.at(i).unicode();
475 if (ch > 0xFF)
476 ch = '?';
477 if ((ch - '0') >= 10 && (ch - 'a') >= 26 && (ch - 'A') >= 26
478 && ch != '/' && ch != '(' && ch != ')' && ch != ',' && ch != '*'
479 && ch != '&' && ch != '_' && ch != '<' && ch != '>' && ch != ':'
480 && ch != '~')
481 result.replace(i, 1, QString("%") + QString("%1").arg(ch, 2, 16));
482 }
483 return result;
484#else
485 return string;
486#endif
487}
488
489QStringList CodeMarker::macRefsForNode(const Node *node)
490{
491 QString result = QLatin1String("cpp/");
492 switch (node->type()) {
493 case Node::Class:
494 {
495 const ClassNode *classe = static_cast<const ClassNode *>(node);
496#if 0
497 if (!classe->templateStuff().isEmpty()) {
498 result += QLatin1String("tmplt/");
499 }
500 else
501#endif
502 {
503 result += QLatin1String("cl/");
504 }
505 result += macName(classe); // ### Maybe plainName?
506 }
507 break;
508 case Node::Enum:
509 {
510 QStringList stringList;
511 stringList << encode(result + QLatin1String("tag/") +
512 macName(node));
513 foreach (const QString &enumName, node->doc().enumItemNames()) {
514 // ### Write a plainEnumValue() and use it here
515 stringList << encode(result + QLatin1String("econst/") +
516 macName(node->parent(), enumName));
517 }
518 return stringList;
519 }
520 case Node::Typedef:
521 result += QLatin1String("tdef/") + macName(node);
522 break;
523 case Node::Function:
524 {
525 bool isMacro = false;
526 const FunctionNode *func = static_cast<const FunctionNode *>(node);
527
528 // overloads are too clever for the Xcode documentation browser
529 if (func->isOverload())
530 return QStringList();
531
532 if (func->metaness() == FunctionNode::MacroWithParams
533 || func->metaness() == FunctionNode::MacroWithoutParams) {
534 result += QLatin1String("macro/");
535 isMacro = true;
536#if 0
537 }
538 else if (!func->templateStuff().isEmpty()) {
539 result += QLatin1String("ftmplt/");
540#endif
541 }
542 else if (func->isStatic()) {
543 result += QLatin1String("clm/");
544 }
545 else if (!func->parent()->name().isEmpty()) {
546 result += QLatin1String("instm/");
547 }
548 else {
549 result += QLatin1String("func/");
550 }
551
552 result += macName(func);
553 if (result.endsWith(QLatin1String("()")))
554 result.chop(2);
555#if 0
556 // this code is too clever for the Xcode documentation
557 // browser and/or pbhelpindexer
558 if (!isMacro) {
559 result += "/" + QLatin1String(QMetaObject::normalizedSignature(func->returnType().toLatin1().constData())) + "/(";
560 const QList<Parameter> &params = func->parameters();
561 for (int i = 0; i < params.count(); ++i) {
562 QString type = params.at(i).leftType() +
563 params.at(i).rightType();
564 type = QLatin1String(QMetaObject::normalizedSignature(type.toLatin1().constData()));
565 if (i != 0)
566 result += ",";
567 result += type;
568 }
569 result += ")";
570 }
571#endif
572 }
573 break;
574 case Node::Variable:
575 result += QLatin1String("data/") + macName(node);
576 break;
577 case Node::Property:
578 {
579 NodeList list = static_cast<const PropertyNode*>(node)->functions();
580 QStringList stringList;
581 foreach (const Node *node, list) {
582 stringList += macRefsForNode(node);
583 }
584 return stringList;
585 }
586 case Node::Namespace:
587 case Node::Fake:
588 case Node::Target:
589 default:
590 return QStringList();
591 }
592
593 return QStringList(encode(result));
594}
595
596QString CodeMarker::macName(const Node *node, const QString &name)
597{
598 QString myName = name;
599 if (myName.isEmpty()) {
600 myName = node->name();
601 node = node->parent();
602 }
603
604 if (node->name().isEmpty()) {
605 return QLatin1Char('/') + protect(myName);
606 }
607 else {
608 return plainFullName(node) + QLatin1Char('/') + protect(myName);
609 }
610}
611
612#ifdef QDOC_QML
613/*!
614 Get the list of documentation sections for the children of
615 the specified QmlClassNode.
616 */
617QList<Section> CodeMarker::qmlSections(const QmlClassNode* , SynopsisStyle )
618{
619 return QList<Section>();
620}
621#endif
622
623QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.