Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /**************************************************************************** |
---|---|
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the tools applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
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 Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | /* |
43 | generator.cpp |
44 | */ |
45 | #include <qdir.h> |
46 | #ifdef DEBUG_MULTIPLE_QDOCCONF_FILES |
47 | #include <qdebug.h> |
48 | #endif |
49 | #include "codemarker.h" |
50 | #include "config.h" |
51 | #include "doc.h" |
52 | #include "editdistance.h" |
53 | #include "generator.h" |
54 | #include "node.h" |
55 | #include "openedlist.h" |
56 | #include "quoter.h" |
57 | #include "separator.h" |
58 | #include "tokenizer.h" |
59 | #include "ditaxmlgenerator.h" |
60 | |
61 | QT_BEGIN_NAMESPACE |
62 | |
63 | QList<Generator *> Generator::generators; |
64 | QMap<QString, QMap<QString, QString> > Generator::fmtLeftMaps; |
65 | QMap<QString, QMap<QString, QString> > Generator::fmtRightMaps; |
66 | QMap<QString, QStringList> Generator::imgFileExts; |
67 | QSet<QString> Generator::outputFormats; |
68 | QStringList Generator::imageFiles; |
69 | QStringList Generator::imageDirs; |
70 | QStringList Generator::exampleDirs; |
71 | QStringList Generator::exampleImgExts; |
72 | QStringList Generator::scriptFiles; |
73 | QStringList Generator::scriptDirs; |
74 | QStringList Generator::styleFiles; |
75 | QStringList Generator::styleDirs; |
76 | QString Generator::outDir; |
77 | QString Generator::project; |
78 | QHash<QString, QString> Generator::outputPrefixes; |
79 | |
80 | QString Generator::sinceTitles[] = |
81 | { |
82 | " New Namespaces", |
83 | " New Classes", |
84 | " New Member Functions", |
85 | " New Functions in Namespaces", |
86 | " New Global Functions", |
87 | " New Macros", |
88 | " New Enum Types", |
89 | " New Typedefs", |
90 | " New Properties", |
91 | " New Variables", |
92 | " New QML Elements", |
93 | " New QML Properties", |
94 | " New QML Signals", |
95 | " New QML Methods", |
96 | "" |
97 | }; |
98 | |
99 | static void singularPlural(Text& text, const NodeList& nodes) |
100 | { |
101 | if (nodes.count() == 1) |
102 | text << " is"; |
103 | else |
104 | text << " are"; |
105 | } |
106 | |
107 | Generator::Generator() |
108 | : amp("&"), |
109 | lt("<"), |
110 | gt(">"), |
111 | quot("""), |
112 | tag("</?@[^>]*>") |
113 | { |
114 | generators.prepend(this); |
115 | } |
116 | |
117 | Generator::~Generator() |
118 | { |
119 | generators.removeAll(this); |
120 | } |
121 | |
122 | void Generator::initializeGenerator(const Config & /* config */) |
123 | { |
124 | } |
125 | |
126 | void Generator::terminateGenerator() |
127 | { |
128 | } |
129 | |
130 | void Generator::initialize(const Config &config) |
131 | { |
132 | outputFormats = config.getStringSet(CONFIG_OUTPUTFORMATS); |
133 | if (!outputFormats.isEmpty()) { |
134 | outDir = config.getString(CONFIG_OUTPUTDIR); |
135 | if (outDir.isEmpty()) |
136 | config.lastLocation().fatal(tr("No output directory specified in configuration file")); |
137 | |
138 | QDir dirInfo; |
139 | if (dirInfo.exists(outDir)) { |
140 | if (!Config::removeDirContents(outDir)) |
141 | config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir)); |
142 | } |
143 | else { |
144 | if (!dirInfo.mkpath(outDir)) |
145 | config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir)); |
146 | } |
147 | |
148 | if (!dirInfo.mkdir(outDir + "/images")) |
149 | config.lastLocation().fatal(tr("Cannot create output directory '%1'") |
150 | .arg(outDir + "/images")); |
151 | if (!dirInfo.mkdir(outDir + "/images/used-in-examples")) |
152 | config.lastLocation().fatal(tr("Cannot create output directory '%1'") |
153 | .arg(outDir + "/images/used-in-examples")); |
154 | if (!dirInfo.mkdir(outDir + "/scripts")) |
155 | config.lastLocation().fatal(tr("Cannot create output directory '%1'") |
156 | .arg(outDir + "/scripts")); |
157 | if (!dirInfo.mkdir(outDir + "/style")) |
158 | config.lastLocation().fatal(tr("Cannot create output directory '%1'") |
159 | .arg(outDir + "/style")); |
160 | } |
161 | |
162 | imageFiles = config.getStringList(CONFIG_IMAGES); |
163 | imageDirs = config.getStringList(CONFIG_IMAGEDIRS); |
164 | scriptFiles = config.getStringList(CONFIG_SCRIPTS); |
165 | scriptDirs = config.getStringList(CONFIG_SCRIPTDIRS); |
166 | styleFiles = config.getStringList(CONFIG_STYLES); |
167 | styleDirs = config.getStringList(CONFIG_STYLEDIRS); |
168 | exampleDirs = config.getStringList(CONFIG_EXAMPLEDIRS); |
169 | exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot + |
170 | CONFIG_IMAGEEXTENSIONS); |
171 | |
172 | QString imagesDotFileExtensions = |
173 | CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS; |
174 | QSet<QString> formats = config.subVars(imagesDotFileExtensions); |
175 | QSet<QString>::ConstIterator f = formats.begin(); |
176 | while (f != formats.end()) { |
177 | imgFileExts[*f] = config.getStringList(imagesDotFileExtensions + |
178 | Config::dot + *f); |
179 | ++f; |
180 | } |
181 | |
182 | QList<Generator *>::ConstIterator g = generators.begin(); |
183 | while (g != generators.end()) { |
184 | if (outputFormats.contains((*g)->format())) { |
185 | (*g)->initializeGenerator(config); |
186 | QStringList extraImages = |
187 | config.getStringList(CONFIG_EXTRAIMAGES+Config::dot+(*g)->format()); |
188 | QStringList::ConstIterator e = extraImages.begin(); |
189 | while (e != extraImages.end()) { |
190 | QString userFriendlyFilePath; |
191 | QString filePath = Config::findFile(config.lastLocation(), |
192 | imageFiles, |
193 | imageDirs, |
194 | *e, |
195 | imgFileExts[(*g)->format()], |
196 | userFriendlyFilePath); |
197 | if (!filePath.isEmpty()) |
198 | Config::copyFile(config.lastLocation(), |
199 | filePath, |
200 | userFriendlyFilePath, |
201 | (*g)->outputDir() + |
202 | "/images"); |
203 | ++e; |
204 | } |
205 | |
206 | // Documentation template handling |
207 | QString templateDir = config.getString( |
208 | (*g)->format() + Config::dot + CONFIG_TEMPLATEDIR); |
209 | |
210 | if (!templateDir.isEmpty()) { |
211 | QStringList noExts; |
212 | QStringList searchDirs = QStringList() << templateDir; |
213 | QStringList scripts = |
214 | config.getStringList((*g)->format()+Config::dot+CONFIG_SCRIPTS); |
215 | e = scripts.begin(); |
216 | while (e != scripts.end()) { |
217 | QString userFriendlyFilePath; |
218 | QString filePath = Config::findFile(config.lastLocation(), |
219 | scriptFiles, |
220 | searchDirs, |
221 | *e, |
222 | noExts, |
223 | userFriendlyFilePath); |
224 | if (!filePath.isEmpty()) |
225 | Config::copyFile(config.lastLocation(), |
226 | filePath, |
227 | userFriendlyFilePath, |
228 | (*g)->outputDir() + |
229 | "/scripts"); |
230 | ++e; |
231 | } |
232 | |
233 | QStringList styles = |
234 | config.getStringList((*g)->format()+Config::dot+CONFIG_STYLESHEETS); |
235 | e = styles.begin(); |
236 | while (e != styles.end()) { |
237 | QString userFriendlyFilePath; |
238 | QString filePath = Config::findFile(config.lastLocation(), |
239 | styleFiles, |
240 | searchDirs, |
241 | *e, |
242 | noExts, |
243 | userFriendlyFilePath); |
244 | if (!filePath.isEmpty()) |
245 | Config::copyFile(config.lastLocation(), |
246 | filePath, |
247 | userFriendlyFilePath, |
248 | (*g)->outputDir() + |
249 | "/style"); |
250 | ++e; |
251 | } |
252 | } |
253 | } |
254 | ++g; |
255 | } |
256 | |
257 | QRegExp secondParamAndAbove("[\2-\7]"); |
258 | QSet<QString> formattingNames = config.subVars(CONFIG_FORMATTING); |
259 | QSet<QString>::ConstIterator n = formattingNames.begin(); |
260 | while (n != formattingNames.end()) { |
261 | QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n; |
262 | |
263 | QSet<QString> formats = config.subVars(formattingDotName); |
264 | QSet<QString>::ConstIterator f = formats.begin(); |
265 | while (f != formats.end()) { |
266 | QString def = config.getString(formattingDotName + |
267 | Config::dot + *f); |
268 | if (!def.isEmpty()) { |
269 | int numParams = Config::numParams(def); |
270 | int numOccs = def.count("\1"); |
271 | |
272 | if (numParams != 1) { |
273 | config.lastLocation().warning(tr("Formatting '%1' must " |
274 | "have exactly one " |
275 | "parameter (found %2)") |
276 | .arg(*n).arg(numParams)); |
277 | } |
278 | else if (numOccs > 1) { |
279 | config.lastLocation().fatal(tr("Formatting '%1' must " |
280 | "contain exactly one " |
281 | "occurrence of '\\1' " |
282 | "(found %2)") |
283 | .arg(*n).arg(numOccs)); |
284 | } |
285 | else { |
286 | int paramPos = def.indexOf("\1"); |
287 | fmtLeftMaps[*f].insert(*n, def.left(paramPos)); |
288 | fmtRightMaps[*f].insert(*n, def.mid(paramPos + 1)); |
289 | } |
290 | } |
291 | ++f; |
292 | } |
293 | ++n; |
294 | } |
295 | |
296 | project = config.getString(CONFIG_PROJECT); |
297 | |
298 | QStringList prefixes = config.getStringList(CONFIG_OUTPUTPREFIXES); |
299 | if (!prefixes.isEmpty()) { |
300 | foreach (QString prefix, prefixes) |
301 | outputPrefixes[prefix] = config.getString( |
302 | CONFIG_OUTPUTPREFIXES + Config::dot + prefix); |
303 | } else |
304 | outputPrefixes[QLatin1String("QML")] = QLatin1String( "qml-"); |
305 | } |
306 | |
307 | void Generator::terminate() |
308 | { |
309 | QList<Generator *>::ConstIterator g = generators.begin(); |
310 | while (g != generators.end()) { |
311 | if (outputFormats.contains((*g)->format())) |
312 | (*g)->terminateGenerator(); |
313 | ++g; |
314 | } |
315 | |
316 | fmtLeftMaps.clear(); |
317 | fmtRightMaps.clear(); |
318 | imgFileExts.clear(); |
319 | imageFiles.clear(); |
320 | imageDirs.clear(); |
321 | outDir = ""; |
322 | QmlClassNode::clear(); |
323 | } |
324 | |
325 | Generator *Generator::generatorForFormat(const QString& format) |
326 | { |
327 | QList<Generator *>::ConstIterator g = generators.begin(); |
328 | while (g != generators.end()) { |
329 | if ((*g)->format() == format) |
330 | return *g; |
331 | ++g; |
332 | } |
333 | return 0; |
334 | } |
335 | |
336 | void Generator::startText(const Node * /* relative */, |
337 | CodeMarker * /* marker */) |
338 | { |
339 | } |
340 | |
341 | void Generator::endText(const Node * /* relative */, |
342 | CodeMarker * /* marker */) |
343 | { |
344 | } |
345 | |
346 | int Generator::generateAtom(const Atom * /* atom */, |
347 | const Node * /* relative */, |
348 | CodeMarker * /* marker */) |
349 | { |
350 | return 0; |
351 | } |
352 | |
353 | void Generator::generateClassLikeNode(const InnerNode * /* classe */, |
354 | CodeMarker * /* marker */) |
355 | { |
356 | } |
357 | |
358 | void Generator::generateFakeNode(const FakeNode * /* fake */, |
359 | CodeMarker * /* marker */) |
360 | { |
361 | } |
362 | |
363 | bool Generator::generateText(const Text& text, |
364 | const Node *relative, |
365 | CodeMarker *marker) |
366 | { |
367 | bool result = false; |
368 | if (text.firstAtom() != 0) { |
369 | int numAtoms = 0; |
370 | startText(relative, marker); |
371 | generateAtomList(text.firstAtom(), |
372 | relative, |
373 | marker, |
374 | true, |
375 | numAtoms); |
376 | endText(relative, marker); |
377 | result = true; |
378 | } |
379 | return result; |
380 | } |
381 | |
382 | #ifdef QDOC_QML |
383 | /*! |
384 | Extract sections of markup text surrounded by \e qmltext |
385 | and \e endqmltext and output them. |
386 | */ |
387 | bool Generator::generateQmlText(const Text& text, |
388 | const Node *relative, |
389 | CodeMarker *marker, |
390 | const QString& /* qmlName */ ) |
391 | { |
392 | const Atom* atom = text.firstAtom(); |
393 | bool result = false; |
394 | |
395 | if (atom != 0) { |
396 | startText(relative, marker); |
397 | while (atom) { |
398 | if (atom->type() != Atom::QmlText) |
399 | atom = atom->next(); |
400 | else { |
401 | atom = atom->next(); |
402 | while (atom && (atom->type() != Atom::EndQmlText)) { |
403 | int n = 1 + generateAtom(atom, relative, marker); |
404 | while (n-- > 0) |
405 | atom = atom->next(); |
406 | } |
407 | } |
408 | } |
409 | endText(relative, marker); |
410 | result = true; |
411 | } |
412 | return result; |
413 | } |
414 | #endif |
415 | |
416 | void Generator::generateBody(const Node *node, CodeMarker *marker) |
417 | { |
418 | bool quiet = false; |
419 | |
420 | if (node->type() == Node::Fake) { |
421 | const FakeNode *fake = static_cast<const FakeNode *>(node); |
422 | if (fake->subType() == Node::Example) { |
423 | generateExampleFiles(fake, marker); |
424 | } |
425 | else if ((fake->subType() == Node::File) || (fake->subType() == Node::Image)) { |
426 | quiet = true; |
427 | } |
428 | } |
429 | |
430 | if (node->doc().isEmpty()) { |
431 | if (!quiet && !node->isReimp()) { // ### might be unnecessary |
432 | node->location().warning(tr("No documentation for '%1'") |
433 | .arg(marker->plainFullName(node))); |
434 | } |
435 | } |
436 | else { |
437 | if (node->type() == Node::Function) { |
438 | const FunctionNode *func = static_cast<const FunctionNode *>(node); |
439 | if (func->reimplementedFrom() != 0) |
440 | generateReimplementedFrom(func, marker); |
441 | } |
442 | |
443 | if (!generateText(node->doc().body(), node, marker)) { |
444 | if (node->isReimp()) |
445 | return; |
446 | } |
447 | |
448 | if (node->type() == Node::Enum) { |
449 | const EnumNode *enume = (const EnumNode *) node; |
450 | |
451 | QSet<QString> definedItems; |
452 | QList<EnumItem>::ConstIterator it = enume->items().begin(); |
453 | while (it != enume->items().end()) { |
454 | definedItems.insert((*it).name()); |
455 | ++it; |
456 | } |
457 | |
458 | QSet<QString> documentedItems = enume->doc().enumItemNames().toSet(); |
459 | QSet<QString> allItems = definedItems + documentedItems; |
460 | if (allItems.count() > definedItems.count() || |
461 | allItems.count() > documentedItems.count()) { |
462 | QSet<QString>::ConstIterator a = allItems.begin(); |
463 | while (a != allItems.end()) { |
464 | if (!definedItems.contains(*a)) { |
465 | QString details; |
466 | QString best = nearestName(*a, definedItems); |
467 | if (!best.isEmpty() && !documentedItems.contains(best)) |
468 | details = tr("Maybe you meant '%1'?").arg(best); |
469 | |
470 | node->doc().location().warning( |
471 | tr("No such enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)), |
472 | details); |
473 | } |
474 | else if (!documentedItems.contains(*a)) { |
475 | node->doc().location().warning( |
476 | tr("Undocumented enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node))); |
477 | } |
478 | ++a; |
479 | } |
480 | } |
481 | } |
482 | else if (node->type() == Node::Function) { |
483 | const FunctionNode *func = static_cast<const FunctionNode *>(node); |
484 | QSet<QString> definedParams; |
485 | QList<Parameter>::ConstIterator p = func->parameters().begin(); |
486 | while (p != func->parameters().end()) { |
487 | if ((*p).name().isEmpty() && (*p).leftType() != QLatin1String("...") |
488 | && func->name() != QLatin1String("operator++") |
489 | && func->name() != QLatin1String("operator--")) { |
490 | node->doc().location().warning(tr("Missing parameter name")); |
491 | } |
492 | else { |
493 | definedParams.insert((*p).name()); |
494 | } |
495 | ++p; |
496 | } |
497 | |
498 | QSet<QString> documentedParams = func->doc().parameterNames(); |
499 | QSet<QString> allParams = definedParams + documentedParams; |
500 | if (allParams.count() > definedParams.count() |
501 | || allParams.count() > documentedParams.count()) { |
502 | QSet<QString>::ConstIterator a = allParams.begin(); |
503 | while (a != allParams.end()) { |
504 | if (!definedParams.contains(*a)) { |
505 | QString details; |
506 | QString best = nearestName(*a, definedParams); |
507 | if (!best.isEmpty()) |
508 | details = tr("Maybe you meant '%1'?").arg(best); |
509 | |
510 | node->doc().location().warning( |
511 | tr("No such parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node)), |
512 | details); |
513 | } |
514 | else if (!(*a).isEmpty() && !documentedParams.contains(*a)) { |
515 | bool needWarning = (func->status() > Node::Obsolete); |
516 | if (func->overloadNumber() > 1) { |
517 | FunctionNode *primaryFunc = |
518 | func->parent()->findFunctionNode(func->name()); |
519 | if (primaryFunc) { |
520 | foreach (const Parameter ¶m, |
521 | primaryFunc->parameters()) { |
522 | if (param.name() == *a) { |
523 | needWarning = false; |
524 | break; |
525 | } |
526 | } |
527 | } |
528 | } |
529 | if (needWarning && !func->isReimp()) |
530 | node->doc().location().warning( |
531 | tr("Undocumented parameter '%1' in %2") |
532 | .arg(*a).arg(marker->plainFullName(node))); |
533 | } |
534 | ++a; |
535 | } |
536 | } |
537 | /* |
538 | Something like this return value check should |
539 | be implemented at some point. |
540 | */ |
541 | if (func->status() > Node::Obsolete && func->returnType() == "bool" |
542 | && func->reimplementedFrom() == 0 && !func->isOverload()) { |
543 | QString body = func->doc().body().toString(); |
544 | if (!body.contains("return", Qt::CaseInsensitive)) |
545 | node->doc().location().warning(tr("Undocumented return value")); |
546 | } |
547 | } |
548 | } |
549 | |
550 | if (node->type() == Node::Fake) { |
551 | const FakeNode *fake = static_cast<const FakeNode *>(node); |
552 | if (fake->subType() == Node::File) { |
553 | Text text; |
554 | Quoter quoter; |
555 | Doc::quoteFromFile(fake->doc().location(), quoter, fake->name()); |
556 | QString code = quoter.quoteTo(fake->location(), "", ""); |
557 | CodeMarker *codeMarker = CodeMarker::markerForFileName(fake->name()); |
558 | text << Atom(codeMarker->atomType(), code); |
559 | generateText(text, fake, codeMarker); |
560 | } |
561 | } |
562 | } |
563 | |
564 | void Generator::generateAlsoList(const Node *node, CodeMarker *marker) |
565 | { |
566 | QList<Text> alsoList = node->doc().alsoList(); |
567 | supplementAlsoList(node, alsoList); |
568 | |
569 | if (!alsoList.isEmpty()) { |
570 | Text text; |
571 | text << Atom::ParaLeft |
572 | << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) |
573 | << "See also " |
574 | << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD); |
575 | |
576 | for (int i = 0; i < alsoList.size(); ++i) |
577 | text << alsoList.at(i) << separator(i, alsoList.size()); |
578 | |
579 | text << Atom::ParaRight; |
580 | generateText(text, node, marker); |
581 | } |
582 | } |
583 | |
584 | /*! |
585 | Generate a list of maintainers in the output |
586 | */ |
587 | void Generator::generateMaintainerList(const InnerNode* node, CodeMarker* marker) |
588 | { |
589 | QStringList sl = getMetadataElements(node,"maintainer"); |
590 | |
591 | if (!sl.isEmpty()) { |
592 | Text text; |
593 | text << Atom::ParaLeft |
594 | << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) |
595 | << "Maintained by: " |
596 | << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD); |
597 | |
598 | for (int i = 0; i < sl.size(); ++i) |
599 | text << sl.at(i) << separator(i, sl.size()); |
600 | |
601 | text << Atom::ParaRight; |
602 | generateText(text, node, marker); |
603 | } |
604 | } |
605 | |
606 | void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker) |
607 | { |
608 | QList<RelatedClass>::ConstIterator r; |
609 | int index; |
610 | |
611 | if (!classe->baseClasses().isEmpty()) { |
612 | Text text; |
613 | text << Atom::ParaLeft |
614 | << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) |
615 | << "Inherits: " |
616 | << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD); |
617 | |
618 | r = classe->baseClasses().begin(); |
619 | index = 0; |
620 | while (r != classe->baseClasses().end()) { |
621 | text << Atom(Atom::LinkNode, CodeMarker::stringForNode((*r).node)) |
622 | << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) |
623 | << Atom(Atom::String, (*r).dataTypeWithTemplateArgs) |
624 | << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
625 | |
626 | if ((*r).access == Node::Protected) { |
627 | text << " (protected)"; |
628 | } |
629 | else if ((*r).access == Node::Private) { |
630 | text << " (private)"; |
631 | } |
632 | text << separator(index++, classe->baseClasses().count()); |
633 | ++r; |
634 | } |
635 | text << Atom::ParaRight; |
636 | generateText(text, classe, marker); |
637 | } |
638 | } |
639 | |
640 | #ifdef QDOC_QML |
641 | /*! |
642 | */ |
643 | void Generator::generateQmlInherits(const QmlClassNode* , CodeMarker* ) |
644 | { |
645 | // stub. |
646 | } |
647 | #endif |
648 | |
649 | void Generator::generateInheritedBy(const ClassNode *classe, |
650 | CodeMarker *marker) |
651 | { |
652 | if (!classe->derivedClasses().isEmpty()) { |
653 | Text text; |
654 | text << Atom::ParaLeft |
655 | << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) |
656 | << "Inherited by: " |
657 | << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD); |
658 | |
659 | appendSortedNames(text, classe, classe->derivedClasses(), marker); |
660 | text << Atom::ParaRight; |
661 | generateText(text, classe, marker); |
662 | } |
663 | } |
664 | |
665 | /*! |
666 | This function is called when the documentation for an |
667 | example is being formatted. It outputs the list of source |
668 | files comprising the example, and the list of images used |
669 | by the example. The images are copied into a subtree of |
670 | \c{...doc/html/images/used-in-examples/...} |
671 | */ |
672 | void Generator::generateFileList(const FakeNode* fake, |
673 | CodeMarker* marker, |
674 | Node::SubType subtype, |
675 | const QString& tag) |
676 | { |
677 | int count = 0; |
678 | Text text; |
679 | OpenedList openedList(OpenedList::Bullet); |
680 | |
681 | text << Atom::ParaLeft << tag << Atom::ParaRight |
682 | << Atom(Atom::ListLeft, openedList.styleString()); |
683 | |
684 | foreach (const Node* child, fake->childNodes()) { |
685 | if (child->subType() == subtype) { |
686 | ++count; |
687 | QString file = child->name(); |
688 | if (subtype == Node::Image) { |
689 | if (!file.isEmpty()) { |
690 | QDir dirInfo; |
691 | QString userFriendlyFilePath; |
692 | QString srcPath = Config::findFile(fake->location(), |
693 | QStringList(), |
694 | exampleDirs, |
695 | file, |
696 | exampleImgExts, |
697 | userFriendlyFilePath); |
698 | userFriendlyFilePath.truncate(userFriendlyFilePath.lastIndexOf( |
699 | |
700 | QString imgOutDir = outDir + "/images/used-in-examples/"+ userFriendlyFilePath; |
701 | if (!dirInfo.mkpath(imgOutDir)) |
702 | fake->location().fatal(tr("Cannot create output directory '%1'") |
703 | .arg(imgOutDir)); |
704 | |
705 | QString imgOutName = Config::copyFile(fake->location(), |
706 | srcPath, |
707 | file, |
708 | imgOutDir); |
709 | } |
710 | |
711 | } |
712 | |
713 | openedList.next(); |
714 | text << Atom(Atom::ListItemNumber, openedList.numberString()) |
715 | << Atom(Atom::ListItemLeft, openedList.styleString()) |
716 | << Atom::ParaLeft |
717 | << Atom(Atom::Link, file) |
718 | << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) |
719 | << file |
720 | << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) |
721 | << Atom::ParaRight |
722 | << Atom(Atom::ListItemRight, openedList.styleString()); |
723 | } |
724 | } |
725 | text << Atom(Atom::ListRight, openedList.styleString()); |
726 | if (count > 0) |
727 | generateText(text, fake, marker); |
728 | } |
729 | |
730 | void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker) |
731 | { |
732 | if (fake->childNodes().isEmpty()) |
733 | return; |
734 | generateFileList(fake, marker, Node::File, QString("Files:")); |
735 | generateFileList(fake, marker, Node::Image, QString("Images:")); |
736 | } |
737 | |
738 | QString Generator::indent(int level, const QString& markedCode) |
739 | { |
740 | if (level == 0) |
741 | return markedCode; |
742 | |
743 | QString t; |
744 | int column = 0; |
745 | |
746 | int i = 0; |
747 | while (i < (int) markedCode.length()) { |
748 | if (markedCode.at(i) == QLatin1Char( |
749 | column = 0; |
750 | } |
751 | else { |
752 | if (column == 0) { |
753 | for (int j = 0; j < level; j++) |
754 | t += QLatin1Char( |
755 | } |
756 | column++; |
757 | } |
758 | t += markedCode.at(i++); |
759 | } |
760 | return t; |
761 | } |
762 | |
763 | QString Generator::plainCode(const QString& markedCode) |
764 | { |
765 | QString t = markedCode; |
766 | t.replace(tag, QString()); |
767 | t.replace(quot, QLatin1String("\"")); |
768 | t.replace(gt, QLatin1String(">")); |
769 | t.replace(lt, QLatin1String("<")); |
770 | t.replace(amp, QLatin1String("&")); |
771 | return t; |
772 | } |
773 | |
774 | QString Generator::typeString(const Node *node) |
775 | { |
776 | switch (node->type()) { |
777 | case Node::Namespace: |
778 | return "namespace"; |
779 | case Node::Class: |
780 | return "class"; |
781 | case Node::Fake: |
782 | { |
783 | switch (node->subType()) { |
784 | case Node::QmlClass: |
785 | return "element"; |
786 | case Node::QmlPropertyGroup: |
787 | return "property group"; |
788 | case Node::QmlBasicType: |
789 | return "type"; |
790 | default: |
791 | return "documentation"; |
792 | } |
793 | } |
794 | case Node::Enum: |
795 | return "enum"; |
796 | case Node::Typedef: |
797 | return "typedef"; |
798 | case Node::Function: |
799 | return "function"; |
800 | case Node::Property: |
801 | return "property"; |
802 | default: |
803 | return "documentation"; |
804 | } |
805 | } |
806 | |
807 | QString Generator::imageFileName(const Node *relative, const QString& fileBase) |
808 | { |
809 | QString userFriendlyFilePath; |
810 | QString filePath = Config::findFile( |
811 | relative->doc().location(), imageFiles, imageDirs, fileBase, |
812 | imgFileExts[format()], userFriendlyFilePath); |
813 | |
814 | if (filePath.isEmpty()) |
815 | return QString(); |
816 | |
817 | QString path = Config::copyFile(relative->doc().location(), |
818 | filePath, |
819 | userFriendlyFilePath, |
820 | outputDir() + QLatin1String("/images")); |
821 | if (path[0] != |
822 | return QLatin1String("images/") + path; |
823 | return QLatin1String("images") + path; |
824 | } |
825 | |
826 | void Generator::setImageFileExtensions(const QStringList& extensions) |
827 | { |
828 | imgFileExts[format()] = extensions; |
829 | } |
830 | |
831 | void Generator::unknownAtom(const Atom *atom) |
832 | { |
833 | Location::internalError(tr("unknown atom type '%1' in %2 generator") |
834 | .arg(atom->typeString()).arg(format())); |
835 | } |
836 | |
837 | bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType) |
838 | { |
839 | return atom->next() != 0 && atom->next()->type() == expectedAtomType; |
840 | } |
841 | |
842 | void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList) |
843 | { |
844 | if (node->type() == Node::Function) { |
845 | const FunctionNode *func = static_cast<const FunctionNode *>(node); |
846 | if (func->overloadNumber() == 1) { |
847 | QString alternateName; |
848 | const FunctionNode *alternateFunc = 0; |
849 | |
850 | if (func->name().startsWith("set") && func->name().size() >= 4) { |
851 | alternateName = func->name()[3].toLower(); |
852 | alternateName += func->name().mid(4); |
853 | alternateFunc = func->parent()->findFunctionNode(alternateName); |
854 | |
855 | if (!alternateFunc) { |
856 | alternateName = "is"+ func->name().mid(3); |
857 | alternateFunc = func->parent()->findFunctionNode(alternateName); |
858 | if (!alternateFunc) { |
859 | alternateName = "has"+ func->name().mid(3); |
860 | alternateFunc = func->parent()->findFunctionNode(alternateName); |
861 | } |
862 | } |
863 | } |
864 | else if (!func->name().isEmpty()) { |
865 | alternateName = "set"; |
866 | alternateName += func->name()[0].toUpper(); |
867 | alternateName += func->name().mid(1); |
868 | alternateFunc = func->parent()->findFunctionNode(alternateName); |
869 | } |
870 | |
871 | if (alternateFunc && alternateFunc->access() != Node::Private) { |
872 | int i; |
873 | for (i = 0; i < alsoList.size(); ++i) { |
874 | if (alsoList.at(i).toString().contains(alternateName)) |
875 | break; |
876 | } |
877 | |
878 | if (i == alsoList.size()) { |
879 | alternateName += "()"; |
880 | |
881 | Text also; |
882 | also << Atom(Atom::Link, alternateName) |
883 | << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) |
884 | << alternateName |
885 | << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
886 | alsoList.prepend(also); |
887 | } |
888 | } |
889 | } |
890 | } |
891 | } |
892 | |
893 | QMap<QString, QString>& Generator::formattingLeftMap() |
894 | { |
895 | return fmtLeftMaps[format()]; |
896 | } |
897 | |
898 | QMap<QString, QString>& Generator::formattingRightMap() |
899 | { |
900 | return fmtRightMaps[format()]; |
901 | } |
902 | |
903 | /* |
904 | Trims trailimng whitespace off the \a string and returns |
905 | the trimmed string. |
906 | */ |
907 | QString Generator::trimmedTrailing(const QString& string) |
908 | { |
909 | QString trimmed = string; |
910 | while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace()) |
911 | trimmed.truncate(trimmed.length() - 1); |
912 | return trimmed; |
913 | } |
914 | |
915 | void Generator::generateStatus(const Node *node, CodeMarker *marker) |
916 | { |
917 | Text text; |
918 | |
919 | switch (node->status()) { |
920 | case Node::Commendable: |
921 | case Node::Main: |
922 | break; |
923 | case Node::Preliminary: |
924 | text << Atom::ParaLeft |
925 | << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) |
926 | << "This " |
927 | << typeString(node) |
928 | << " is under development and is subject to change." |
929 | << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) |
930 | << Atom::ParaRight; |
931 | break; |
932 | case Node::Deprecated: |
933 | text << Atom::ParaLeft; |
934 | if (node->isInnerNode()) |
935 | text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD); |
936 | text << "This "<< typeString(node) << " is deprecated."; |
937 | if (node->isInnerNode()) |
938 | text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD); |
939 | text << Atom::ParaRight; |
940 | break; |
941 | case Node::Obsolete: |
942 | text << Atom::ParaLeft; |
943 | if (node->isInnerNode()) |
944 | text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD); |
945 | text << "This "<< typeString(node) << " is obsolete."; |
946 | if (node->isInnerNode()) |
947 | text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD); |
948 | text << " It is provided to keep old source code working. " |
949 | << "We strongly advise against " |
950 | << "using it in new code."<< Atom::ParaRight; |
951 | break; |
952 | case Node::Compat: |
953 | // reimplemented in HtmlGenerator subclass |
954 | if (node->isInnerNode()) { |
955 | text << Atom::ParaLeft |
956 | << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) |
957 | << "This " |
958 | << typeString(node) |
959 | << " is part of the Qt 3 compatibility layer." |
960 | << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) |
961 | << " It is provided to keep old source code working. " |
962 | << "We strongly advise against " |
963 | << "using it in new code. See " |
964 | << Atom(Atom::AutoLink, "Porting to Qt 4") |
965 | << " for more information." |
966 | << Atom::ParaRight; |
967 | } |
968 | break; |
969 | case Node::Internal: |
970 | default: |
971 | break; |
972 | } |
973 | generateText(text, node, marker); |
974 | } |
975 | |
976 | void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker) |
977 | { |
978 | Text text; |
979 | Text theStockLink; |
980 | Node::ThreadSafeness threadSafeness = node->threadSafeness(); |
981 | |
982 | Text rlink; |
983 | rlink << Atom(Atom::Link,"reentrant") |
984 | << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) |
985 | << "reentrant" |
986 | << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
987 | |
988 | Text tlink; |
989 | tlink << Atom(Atom::Link,"thread-safe") |
990 | << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) |
991 | << "thread-safe" |
992 | << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
993 | |
994 | switch (threadSafeness) { |
995 | case Node::UnspecifiedSafeness: |
996 | break; |
997 | case Node::NonReentrant: |
998 | text << Atom::ParaLeft |
999 | << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) |
1000 | << "Warning:" |
1001 | << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD) |
1002 | << " This " |
1003 | << typeString(node) |
1004 | << " is not " |
1005 | << rlink |
1006 | << "." |
1007 | << Atom::ParaRight; |
1008 | break; |
1009 | case Node::Reentrant: |
1010 | case Node::ThreadSafe: |
1011 | text << Atom::ParaLeft |
1012 | << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) |
1013 | << "Note:" |
1014 | << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD) |
1015 | << " "; |
1016 | |
1017 | if (node->isInnerNode()) { |
1018 | const InnerNode* innerNode = static_cast<const InnerNode*>(node); |
1019 | text << "All functions in this " |
1020 | << typeString(node) |
1021 | << " are "; |
1022 | if (threadSafeness == Node::ThreadSafe) |
1023 | text << tlink; |
1024 | else |
1025 | text << rlink; |
1026 | |
1027 | bool exceptions = false; |
1028 | NodeList reentrant; |
1029 | NodeList threadsafe; |
1030 | NodeList nonreentrant; |
1031 | NodeList::ConstIterator c = innerNode->childNodes().begin(); |
1032 | while (c != innerNode->childNodes().end()) { |
1033 | |
1034 | if ((*c)->status() != Node::Obsolete){ |
1035 | switch ((*c)->threadSafeness()) { |
1036 | case Node::Reentrant: |
1037 | reentrant.append(*c); |
1038 | if (threadSafeness == Node::ThreadSafe) |
1039 | exceptions = true; |
1040 | break; |
1041 | case Node::ThreadSafe: |
1042 | threadsafe.append(*c); |
1043 | if (threadSafeness == Node::Reentrant) |
1044 | exceptions = true; |
1045 | break; |
1046 | case Node::NonReentrant: |
1047 | nonreentrant.append(*c); |
1048 | exceptions = true; |
1049 | break; |
1050 | default: |
1051 | break; |
1052 | } |
1053 | } |
1054 | ++c; |
1055 | } |
1056 | if (!exceptions) |
1057 | text << "."; |
1058 | else if (threadSafeness == Node::Reentrant) { |
1059 | if (nonreentrant.isEmpty()) { |
1060 | if (!threadsafe.isEmpty()) { |
1061 | text << ", but "; |
1062 | appendFullNames(text,threadsafe,innerNode,marker); |
1063 | singularPlural(text,threadsafe); |
1064 | text << " also "<< tlink << "."; |
1065 | } |
1066 | else |
1067 | text << "."; |
1068 | } |
1069 | else { |
1070 | text << ", except for "; |
1071 | appendFullNames(text,nonreentrant,innerNode,marker); |
1072 | text << ", which"; |
1073 | singularPlural(text,nonreentrant); |
1074 | text << " nonreentrant."; |
1075 | if (!threadsafe.isEmpty()) { |
1076 | text << " "; |
1077 | appendFullNames(text,threadsafe,innerNode,marker); |
1078 | singularPlural(text,threadsafe); |
1079 | text << " "<< tlink << "."; |
1080 | } |
1081 | } |
1082 | } |
1083 | else { // thread-safe |
1084 | if (!nonreentrant.isEmpty() || !reentrant.isEmpty()) { |
1085 | text << ", except for "; |
1086 | if (!reentrant.isEmpty()) { |
1087 | appendFullNames(text,reentrant,innerNode,marker); |
1088 | text << ", which"; |
1089 | singularPlural(text,reentrant); |
1090 | text << " only "<< rlink; |
1091 | if (!nonreentrant.isEmpty()) |
1092 | text << ", and "; |
1093 | } |
1094 | if (!nonreentrant.isEmpty()) { |
1095 | appendFullNames(text,nonreentrant,innerNode,marker); |
1096 | text << ", which"; |
1097 | singularPlural(text,nonreentrant); |
1098 | text << " nonreentrant."; |
1099 | } |
1100 | text << "."; |
1101 | } |
1102 | } |
1103 | } |
1104 | else { |
1105 | text << "This "<< typeString(node) << " is "; |
1106 | if (threadSafeness == Node::ThreadSafe) |
1107 | text << tlink; |
1108 | else |
1109 | text << rlink; |
1110 | text << "."; |
1111 | } |
1112 | text << Atom::ParaRight; |
1113 | } |
1114 | generateText(text,node,marker); |
1115 | } |
1116 | |
1117 | void Generator::generateSince(const Node *node, CodeMarker *marker) |
1118 | { |
1119 | if (!node->since().isEmpty()) { |
1120 | Text text; |
1121 | text << Atom::ParaLeft |
1122 | << "This " |
1123 | << typeString(node); |
1124 | if (node->type() == Node::Enum) |
1125 | text << " was introduced or modified in "; |
1126 | else |
1127 | text << " was introduced in "; |
1128 | |
1129 | QStringList since = node->since().split(" "); |
1130 | if (since.count() == 1) { |
1131 | // Handle legacy use of \since <version>. |
1132 | if (project.isEmpty()) |
1133 | text << "version"; |
1134 | else |
1135 | text << project; |
1136 | text << " "<< since[0]; |
1137 | } else { |
1138 | // Reconstruct the <project> <version> string. |
1139 | text << " "<< since.join( " "); |
1140 | } |
1141 | |
1142 | text << "."<< Atom::ParaRight; |
1143 | generateText(text, node, marker); |
1144 | } |
1145 | } |
1146 | |
1147 | void Generator::generateReimplementedFrom(const FunctionNode *func, |
1148 | CodeMarker *marker) |
1149 | { |
1150 | if (func->reimplementedFrom() != 0) { |
1151 | const FunctionNode *from = func->reimplementedFrom(); |
1152 | if (from->access() != Node::Private && |
1153 | from->parent()->access() != Node::Private) { |
1154 | Text text; |
1155 | text << Atom::ParaLeft << "Reimplemented from "; |
1156 | QString fullName = from->parent()->name() + "::"+ from->name() + "()"; |
1157 | appendFullName(text, from->parent(), fullName, from); |
1158 | text << "."<< Atom::ParaRight; |
1159 | generateText(text, func, marker); |
1160 | } |
1161 | } |
1162 | } |
1163 | |
1164 | const Atom *Generator::generateAtomList(const Atom *atom, |
1165 | const Node *relative, |
1166 | CodeMarker *marker, |
1167 | bool generate, |
1168 | int &numAtoms) |
1169 | { |
1170 | while (atom) { |
1171 | if (atom->type() == Atom::FormatIf) { |
1172 | int numAtoms0 = numAtoms; |
1173 | bool rightFormat = canHandleFormat(atom->string()); |
1174 | atom = generateAtomList(atom->next(), |
1175 | relative, |
1176 | marker, |
1177 | generate && rightFormat, |
1178 | numAtoms); |
1179 | if (!atom) |
1180 | return 0; |
1181 | |
1182 | if (atom->type() == Atom::FormatElse) { |
1183 | ++numAtoms; |
1184 | atom = generateAtomList(atom->next(), |
1185 | relative, |
1186 | marker, |
1187 | generate && !rightFormat, |
1188 | numAtoms); |
1189 | if (!atom) |
1190 | return 0; |
1191 | } |
1192 | |
1193 | if (atom->type() == Atom::FormatEndif) { |
1194 | if (generate && numAtoms0 == numAtoms) { |
1195 | relative->location().warning(tr("Output format %1 not handled %2") |
1196 | .arg(format()).arg(outFileName())); |
1197 | Atom unhandledFormatAtom(Atom::UnhandledFormat, format()); |
1198 | generateAtomList(&unhandledFormatAtom, |
1199 | relative, |
1200 | marker, |
1201 | generate, |
1202 | numAtoms); |
1203 | } |
1204 | atom = atom->next(); |
1205 | } |
1206 | } |
1207 | else if (atom->type() == Atom::FormatElse || |
1208 | atom->type() == Atom::FormatEndif) { |
1209 | return atom; |
1210 | } |
1211 | else { |
1212 | int n = 1; |
1213 | if (generate) { |
1214 | n += generateAtom(atom, relative, marker); |
1215 | numAtoms += n; |
1216 | } |
1217 | while (n-- > 0) |
1218 | atom = atom->next(); |
1219 | } |
1220 | } |
1221 | return 0; |
1222 | } |
1223 | |
1224 | void Generator::appendFullName(Text& text, |
1225 | const Node *apparentNode, |
1226 | const Node *relative, |
1227 | CodeMarker *marker, |
1228 | const Node *actualNode) |
1229 | { |
1230 | if (actualNode == 0) |
1231 | actualNode = apparentNode; |
1232 | text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode)) |
1233 | << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) |
1234 | << Atom(Atom::String, marker->plainFullName(apparentNode, relative)) |
1235 | << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
1236 | } |
1237 | |
1238 | void Generator::appendFullName(Text& text, |
1239 | const Node *apparentNode, |
1240 | const QString& fullName, |
1241 | const Node *actualNode) |
1242 | { |
1243 | if (actualNode == 0) |
1244 | actualNode = apparentNode; |
1245 | text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode)) |
1246 | << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) |
1247 | << Atom(Atom::String, fullName) |
1248 | << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
1249 | } |
1250 | |
1251 | void Generator::appendFullNames(Text& text, |
1252 | const NodeList& nodes, |
1253 | const Node* relative, |
1254 | CodeMarker* marker) |
1255 | { |
1256 | NodeList::ConstIterator n = nodes.begin(); |
1257 | int index = 0; |
1258 | while (n != nodes.end()) { |
1259 | appendFullName(text,*n,relative,marker); |
1260 | text << comma(index++,nodes.count()); |
1261 | ++n; |
1262 | } |
1263 | } |
1264 | |
1265 | void Generator::appendSortedNames(Text& text, |
1266 | const ClassNode *classe, |
1267 | const QList<RelatedClass> &classes, |
1268 | CodeMarker *marker) |
1269 | { |
1270 | QList<RelatedClass>::ConstIterator r; |
1271 | QMap<QString,Text> classMap; |
1272 | int index = 0; |
1273 | |
1274 | r = classes.begin(); |
1275 | while (r != classes.end()) { |
1276 | if ((*r).node->access() == Node::Public && |
1277 | (*r).node->status() != Node::Internal |
1278 | && !(*r).node->doc().isEmpty()) { |
1279 | Text className; |
1280 | appendFullName(className, (*r).node, classe, marker); |
1281 | classMap[className.toString().toLower()] = className; |
1282 | } |
1283 | ++r; |
1284 | } |
1285 | |
1286 | QStringList classNames = classMap.keys(); |
1287 | classNames.sort(); |
1288 | |
1289 | foreach (const QString &className, classNames) { |
1290 | text << classMap[className]; |
1291 | text << separator(index++, classNames.count()); |
1292 | } |
1293 | } |
1294 | |
1295 | void Generator::appendSortedQmlNames(Text& text, |
1296 | const Node* base, |
1297 | const NodeList& subs, |
1298 | CodeMarker *marker) |
1299 | { |
1300 | QMap<QString,Text> classMap; |
1301 | int index = 0; |
1302 | |
1303 | #ifdef DEBUG_MULTIPLE_QDOCCONF_FILES |
1304 | qDebug() << "Generator::appendSortedQmlNames():"<< base->name() << "is inherited by..."; |
1305 | #endif |
1306 | for (int i = 0; i < subs.size(); ++i) { |
1307 | Text t; |
1308 | #ifdef DEBUG_MULTIPLE_QDOCCONF_FILES |
1309 | qDebug() << " "<< subs[i]->name(); |
1310 | #endif |
1311 | appendFullName(t, subs[i], base, marker); |
1312 | classMap[t.toString().toLower()] = t; |
1313 | } |
1314 | |
1315 | QStringList names = classMap.keys(); |
1316 | names.sort(); |
1317 | |
1318 | foreach (const QString &name, names) { |
1319 | text << classMap[name]; |
1320 | text << separator(index++, names.count()); |
1321 | } |
1322 | } |
1323 | |
1324 | int Generator::skipAtoms(const Atom *atom, Atom::Type type) const |
1325 | { |
1326 | int skipAhead = 0; |
1327 | atom = atom->next(); |
1328 | while (atom != 0 && atom->type() != type) { |
1329 | skipAhead++; |
1330 | atom = atom->next(); |
1331 | } |
1332 | return skipAhead; |
1333 | } |
1334 | |
1335 | QString Generator::fullName(const Node *node, |
1336 | const Node *relative, |
1337 | CodeMarker *marker) const |
1338 | { |
1339 | if (node->type() == Node::Fake) |
1340 | return static_cast<const FakeNode *>(node)->title(); |
1341 | else if (node->type() == Node::Class && |
1342 | !(static_cast<const ClassNode *>(node))->serviceName().isEmpty()) |
1343 | return (static_cast<const ClassNode *>(node))->serviceName(); |
1344 | else |
1345 | return marker->plainFullName(node, relative); |
1346 | } |
1347 | |
1348 | QString Generator::outputPrefix(const QString &nodeType) |
1349 | { |
1350 | return outputPrefixes[nodeType]; |
1351 | } |
1352 | |
1353 | /*! |
1354 | Looks up the tag \a t in the map of metadata values for the |
1355 | current topic in \a inner. If a value for the tag is found, |
1356 | the value is returned. |
1357 | |
1358 | \note If \a t is found in the metadata map, it is erased. |
1359 | i.e. Once you call this function for a particular \a t, |
1360 | you consume \a t. |
1361 | */ |
1362 | QString Generator::getMetadataElement(const InnerNode* inner, const QString& t) |
1363 | { |
1364 | QString s; |
1365 | QStringMultiMap& metaTagMap = const_cast<QStringMultiMap&>(inner->doc().metaTagMap()); |
1366 | QStringMultiMap::iterator i = metaTagMap.find(t); |
1367 | if (i != metaTagMap.end()) { |
1368 | s = i.value(); |
1369 | metaTagMap.erase(i); |
1370 | } |
1371 | return s; |
1372 | } |
1373 | |
1374 | /*! |
1375 | Looks up the tag \a t in the map of metadata values for the |
1376 | current topic in \a inner. If values for the tag are found, |
1377 | they are returned in a string list. |
1378 | |
1379 | \note If \a t is found in the metadata map, all the pairs |
1380 | having the key \a t are erased. i.e. Once you call this |
1381 | function for a particular \a t, you consume \a t. |
1382 | */ |
1383 | QStringList Generator::getMetadataElements(const InnerNode* inner, const QString& t) |
1384 | { |
1385 | QStringList s; |
1386 | QStringMultiMap& metaTagMap = const_cast<QStringMultiMap&>(inner->doc().metaTagMap()); |
1387 | s = metaTagMap.values(t); |
1388 | if (!s.isEmpty()) |
1389 | metaTagMap.remove(t); |
1390 | return s; |
1391 | } |
1392 | |
1393 | /*! |
1394 | For generating the "New Classes... in 4.6" section on the |
1395 | What's New in 4.6" page. |
1396 | */ |
1397 | void Generator::findAllSince(const InnerNode *node) |
1398 | { |
1399 | NodeList::const_iterator child = node->childNodes().constBegin(); |
1400 | |
1401 | // Traverse the tree, starting at the node supplied. |
1402 | |
1403 | while (child != node->childNodes().constEnd()) { |
1404 | |
1405 | QString sinceString = (*child)->since(); |
1406 | |
1407 | if (((*child)->access() != Node::Private) && !sinceString.isEmpty()) { |
1408 | |
1409 | // Insert a new entry into each map for each new since string found. |
1410 | NewSinceMaps::iterator nsmap = newSinceMaps.find(sinceString); |
1411 | if (nsmap == newSinceMaps.end()) |
1412 | nsmap = newSinceMaps.insert(sinceString,NodeMultiMap()); |
1413 | |
1414 | NewClassMaps::iterator ncmap = newClassMaps.find(sinceString); |
1415 | if (ncmap == newClassMaps.end()) |
1416 | ncmap = newClassMaps.insert(sinceString,NodeMap()); |
1417 | |
1418 | NewClassMaps::iterator nqcmap = newQmlClassMaps.find(sinceString); |
1419 | if (nqcmap == newQmlClassMaps.end()) |
1420 | nqcmap = newQmlClassMaps.insert(sinceString,NodeMap()); |
1421 | |
1422 | if ((*child)->type() == Node::Function) { |
1423 | // Insert functions into the general since map. |
1424 | FunctionNode *func = static_cast<FunctionNode *>(*child); |
1425 | if ((func->status() > Node::Obsolete) && |
1426 | (func->metaness() != FunctionNode::Ctor) && |
1427 | (func->metaness() != FunctionNode::Dtor)) { |
1428 | nsmap.value().insert(func->name(),(*child)); |
1429 | } |
1430 | } |
1431 | else if ((*child)->url().isEmpty()) { |
1432 | if ((*child)->type() == Node::Class && !(*child)->doc().isEmpty()) { |
1433 | // Insert classes into the since and class maps. |
1434 | QString className = (*child)->name(); |
1435 | if ((*child)->parent() && |
1436 | (*child)->parent()->type() == Node::Namespace && |
1437 | !(*child)->parent()->name().isEmpty()) |
1438 | className = (*child)->parent()->name()+"::"+className; |
1439 | |
1440 | nsmap.value().insert(className,(*child)); |
1441 | ncmap.value().insert(className,(*child)); |
1442 | } |
1443 | else if ((*child)->subType() == Node::QmlClass) { |
1444 | // Insert QML elements into the since and element maps. |
1445 | QString className = (*child)->name(); |
1446 | if ((*child)->parent() && |
1447 | (*child)->parent()->type() == Node::Namespace && |
1448 | !(*child)->parent()->name().isEmpty()) |
1449 | className = (*child)->parent()->name()+"::"+className; |
1450 | |
1451 | nsmap.value().insert(className,(*child)); |
1452 | nqcmap.value().insert(className,(*child)); |
1453 | } |
1454 | else if ((*child)->type() == Node::QmlProperty) { |
1455 | // Insert QML properties into the since map. |
1456 | QString propertyName = (*child)->name(); |
1457 | nsmap.value().insert(propertyName,(*child)); |
1458 | } |
1459 | } |
1460 | else { |
1461 | // Insert external documents into the general since map. |
1462 | QString name = (*child)->name(); |
1463 | if ((*child)->parent() && |
1464 | (*child)->parent()->type() == Node::Namespace && |
1465 | !(*child)->parent()->name().isEmpty()) |
1466 | name = (*child)->parent()->name()+"::"+name; |
1467 | |
1468 | nsmap.value().insert(name,(*child)); |
1469 | } |
1470 | |
1471 | // Find child nodes with since commands. |
1472 | if ((*child)->isInnerNode()) { |
1473 | findAllSince(static_cast<InnerNode *>(*child)); |
1474 | } |
1475 | } |
1476 | ++child; |
1477 | } |
1478 | } |
1479 | |
1480 | QT_END_NAMESPACE |
1481 |
Warning: That file was not part of the compilation database. It may have many parsing errors.