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 | ditaxmlgenerator.cpp |
44 | */ |
45 | |
46 | #include "codemarker.h" |
47 | #include "codeparser.h" |
48 | #include "ditaxmlgenerator.h" |
49 | #include "node.h" |
50 | #include "quoter.h" |
51 | #include "separator.h" |
52 | #include "tree.h" |
53 | #include <ctype.h> |
54 | #include <qdebug.h> |
55 | #include <qlist.h> |
56 | #include <qiterator.h> |
57 | #include <qtextcodec.h> |
58 | #include <QUuid> |
59 | |
60 | QT_BEGIN_NAMESPACE |
61 | |
62 | #define COMMAND_VERSION Doc::alias("version") |
63 | int DitaXmlGenerator::id = 0; |
64 | |
65 | /* |
66 | The strings in this array must appear in the same order as |
67 | the values in enum DitaXmlGenerator::DitaTag. |
68 | */ |
69 | QString DitaXmlGenerator::ditaTags[] = |
70 | { |
71 | "", |
72 | "alt", |
73 | "apiDesc", |
74 | "APIMap", |
75 | "apiName", |
76 | "apiRelation", |
77 | "audience", |
78 | "author", |
79 | "b", |
80 | "body", |
81 | "bodydiv", |
82 | "brand", |
83 | "category", |
84 | "codeblock", |
85 | "comment", |
86 | "component", |
87 | "copyrholder", |
88 | "copyright", |
89 | "copyryear", |
90 | "created", |
91 | "critdates", |
92 | "cxxAPIMap", |
93 | "cxxClass", |
94 | "cxxClassAbstract", |
95 | "cxxClassAccessSpecifier", |
96 | "cxxClassAPIItemLocation", |
97 | "cxxClassBaseClass", |
98 | "cxxClassDeclarationFile", |
99 | "cxxClassDeclarationFileLine", |
100 | "cxxClassDefinition", |
101 | "cxxClassDerivation", |
102 | "cxxClassDerivationAccessSpecifier", |
103 | "cxxClassDerivations", |
104 | "cxxClassDetail", |
105 | "cxxClassNested", |
106 | "cxxClassNestedClass", |
107 | "cxxClassNestedDetail", |
108 | "cxxDefine", |
109 | "cxxDefineAccessSpecifier", |
110 | "cxxDefineAPIItemLocation", |
111 | "cxxDefineDeclarationFile", |
112 | "cxxDefineDeclarationFileLine", |
113 | "cxxDefineDefinition", |
114 | "cxxDefineDetail", |
115 | "cxxDefineNameLookup", |
116 | "cxxDefineParameter", |
117 | "cxxDefineParameterDeclarationName", |
118 | "cxxDefineParameters", |
119 | "cxxDefinePrototype", |
120 | "cxxDefineReimplemented", |
121 | "cxxEnumeration", |
122 | "cxxEnumerationAccessSpecifier", |
123 | "cxxEnumerationAPIItemLocation", |
124 | "cxxEnumerationDeclarationFile", |
125 | "cxxEnumerationDeclarationFileLine", |
126 | "cxxEnumerationDefinition", |
127 | "cxxEnumerationDefinitionFile", |
128 | "cxxEnumerationDefinitionFileLineStart", |
129 | "cxxEnumerationDefinitionFileLineEnd", |
130 | "cxxEnumerationDetail", |
131 | "cxxEnumerationNameLookup", |
132 | "cxxEnumerationPrototype", |
133 | "cxxEnumerationScopedName", |
134 | "cxxEnumerator", |
135 | "cxxEnumeratorInitialiser", |
136 | "cxxEnumeratorNameLookup", |
137 | "cxxEnumeratorPrototype", |
138 | "cxxEnumerators", |
139 | "cxxEnumeratorScopedName", |
140 | "cxxFunction", |
141 | "cxxFunctionAccessSpecifier", |
142 | "cxxFunctionAPIItemLocation", |
143 | "cxxFunctionConst", |
144 | "cxxFunctionConstructor", |
145 | "cxxFunctionDeclarationFile", |
146 | "cxxFunctionDeclarationFileLine", |
147 | "cxxFunctionDeclaredType", |
148 | "cxxFunctionDefinition", |
149 | "cxxFunctionDestructor", |
150 | "cxxFunctionDetail", |
151 | "cxxFunctionNameLookup", |
152 | "cxxFunctionParameter", |
153 | "cxxFunctionParameterDeclarationName", |
154 | "cxxFunctionParameterDeclaredType", |
155 | "cxxFunctionParameterDefaultValue", |
156 | "cxxFunctionParameters", |
157 | "cxxFunctionPrototype", |
158 | "cxxFunctionPureVirtual", |
159 | "cxxFunctionReimplemented", |
160 | "cxxFunctionScopedName", |
161 | "cxxFunctionStorageClassSpecifierStatic", |
162 | "cxxFunctionVirtual", |
163 | "cxxTypedef", |
164 | "cxxTypedefAccessSpecifier", |
165 | "cxxTypedefAPIItemLocation", |
166 | "cxxTypedefDeclarationFile", |
167 | "cxxTypedefDeclarationFileLine", |
168 | "cxxTypedefDefinition", |
169 | "cxxTypedefDetail", |
170 | "cxxTypedefNameLookup", |
171 | "cxxTypedefScopedName", |
172 | "cxxVariable", |
173 | "cxxVariableAccessSpecifier", |
174 | "cxxVariableAPIItemLocation", |
175 | "cxxVariableDeclarationFile", |
176 | "cxxVariableDeclarationFileLine", |
177 | "cxxVariableDeclaredType", |
178 | "cxxVariableDefinition", |
179 | "cxxVariableDetail", |
180 | "cxxVariableNameLookup", |
181 | "cxxVariablePrototype", |
182 | "cxxVariableReimplemented", |
183 | "cxxVariableScopedName", |
184 | "cxxVariableStorageClassSpecifierStatic", |
185 | "data", |
186 | "data-about", |
187 | "dd", |
188 | "dl", |
189 | "dlentry", |
190 | "dt", |
191 | "entry", |
192 | "fig", |
193 | "i", |
194 | "image", |
195 | "keyword", |
196 | "keywords", |
197 | "li", |
198 | "link", |
199 | "linktext", |
200 | "lq", |
201 | "metadata", |
202 | "ol", |
203 | "othermeta", |
204 | "p", |
205 | "parameter", |
206 | "permissions", |
207 | "ph", |
208 | "platform", |
209 | "pre", |
210 | "prodinfo", |
211 | "prodname", |
212 | "prolog", |
213 | "publisher", |
214 | "related-links", |
215 | "resourceid", |
216 | "revised", |
217 | "row", |
218 | "section", |
219 | "sectiondiv", |
220 | "shortdesc", |
221 | "simpletable", |
222 | "source", |
223 | "stentry", |
224 | "sthead", |
225 | "strow", |
226 | "sub", |
227 | "sup", |
228 | "table", |
229 | "tbody", |
230 | "tgroup", |
231 | "thead", |
232 | "title", |
233 | "tm", |
234 | "topic", |
235 | "topicmeta", |
236 | "topicref", |
237 | "tt", |
238 | "u", |
239 | "ul", |
240 | "unknown", |
241 | "vrm", |
242 | "vrmlist", |
243 | "xref", |
244 | "" |
245 | }; |
246 | |
247 | static bool showBrokenLinks = false; |
248 | |
249 | /*! |
250 | Quick, dirty, and very ugly. Unescape \a text |
251 | so QXmlStreamWriter::writeCharacters() can put |
252 | the escapes back in again! |
253 | */ |
254 | void DitaXmlGenerator::writeCharacters(const QString& text) |
255 | { |
256 | QString t = text; |
257 | t = t.replace("<", "<"); |
258 | t = t.replace(">", ">"); |
259 | t = t.replace("&", "&"); |
260 | t = t.replace(""", "\""); |
261 | xmlWriter().writeCharacters(t); |
262 | } |
263 | |
264 | /*! |
265 | Appends an <xref> element to the current XML stream |
266 | with the \a href attribute and the \a text. |
267 | */ |
268 | void DitaXmlGenerator::addLink(const QString& href, |
269 | const QStringRef& text, |
270 | DitaTag t) |
271 | { |
272 | if (!href.isEmpty()) { |
273 | writeStartTag(t); |
274 | // formathtml |
275 | xmlWriter().writeAttribute("href", href); |
276 | writeCharacters(text.toString()); |
277 | writeEndTag(); // </t> |
278 | } |
279 | else { |
280 | writeCharacters(text.toString()); |
281 | } |
282 | } |
283 | |
284 | /*! |
285 | Push \a t onto the dita tag stack and write the appropriate |
286 | start tag to the DITA XML file. |
287 | */ |
288 | void DitaXmlGenerator::writeStartTag(DitaTag t) |
289 | { |
290 | xmlWriter().writeStartElement(ditaTags[t]); |
291 | tagStack.push(t); |
292 | } |
293 | |
294 | /*! |
295 | Pop the current DITA tag off the stack, and write the |
296 | appropriate end tag to the DITA XML file. |
297 | */ |
298 | void DitaXmlGenerator::writeEndTag(DitaTag t) |
299 | { |
300 | DitaTag top = tagStack.pop(); |
301 | if (t > DT_NONE && top != t) |
302 | qDebug() << "Expected:"<< t << "ACTUAL:"<< top; |
303 | xmlWriter().writeEndElement(); |
304 | } |
305 | |
306 | /*! |
307 | Return the current DITA element tag, the one |
308 | on top of the stack. |
309 | */ |
310 | DitaXmlGenerator::DitaTag DitaXmlGenerator::currentTag() |
311 | { |
312 | return tagStack.top(); |
313 | } |
314 | |
315 | /*! |
316 | Write the start tag \c{<apiDesc>}. if \a title is not |
317 | empty, generate a GUID from it and write the GUID as the |
318 | value of the \e{id} attribute. Then write \a title as |
319 | the value of the \e {spectitle} attribute. |
320 | |
321 | Then if \a outputclass is not empty, write it as the value |
322 | of the \a outputclass attribute. |
323 | |
324 | Fiunally, set the section nesting level to 1 and return 1. |
325 | */ |
326 | int DitaXmlGenerator::enterApiDesc(const QString& outputclass, const QString& title) |
327 | { |
328 | writeStartTag(DT_apiDesc); |
329 | if (!title.isEmpty()) { |
330 | writeGuidAttribute(title); |
331 | xmlWriter().writeAttribute("spectitle",title); |
332 | } |
333 | if (!outputclass.isEmpty()) |
334 | xmlWriter().writeAttribute("outputclass",outputclass); |
335 | sectionNestingLevel = 1; |
336 | return sectionNestingLevel; |
337 | } |
338 | |
339 | /*! |
340 | If the section nesting level is 0, output a \c{<section>} |
341 | element with an \e id attribute generated from \a title and |
342 | an \e outputclass attribute set to \a outputclass. |
343 | If \a title is null, no \e id attribute is output. |
344 | If \a outputclass is empty, no \e outputclass attribute |
345 | is output. |
346 | |
347 | Finally, increment the section nesting level and return |
348 | the new value. |
349 | */ |
350 | int DitaXmlGenerator::enterSection(const QString& outputclass, const QString& title) |
351 | { |
352 | if (sectionNestingLevel == 0) { |
353 | writeStartTag(DT_section); |
354 | if (!title.isEmpty()) |
355 | writeGuidAttribute(title); |
356 | if (!outputclass.isEmpty()) |
357 | xmlWriter().writeAttribute("outputclass",outputclass); |
358 | } |
359 | else if (!title.isEmpty()) { |
360 | writeStartTag(DT_p); |
361 | writeGuidAttribute(title); |
362 | if (!outputclass.isEmpty()) |
363 | xmlWriter().writeAttribute("outputclass",outputclass); |
364 | writeCharacters(title); |
365 | writeEndTag(); // </p> |
366 | } |
367 | return ++sectionNestingLevel; |
368 | } |
369 | |
370 | /*! |
371 | If the section nesting level is greater than 0, decrement |
372 | it. If it becomes 0, output a \c {</section>}. Return the |
373 | decremented section nesting level. |
374 | */ |
375 | int DitaXmlGenerator::leaveSection() |
376 | { |
377 | if (sectionNestingLevel > 0) { |
378 | --sectionNestingLevel; |
379 | if (sectionNestingLevel == 0) |
380 | writeEndTag(); // </section> or </apiDesc> |
381 | } |
382 | return sectionNestingLevel; |
383 | } |
384 | |
385 | /*! |
386 | The default constructor. |
387 | */ |
388 | DitaXmlGenerator::DitaXmlGenerator() |
389 | : inContents(false), |
390 | inDetailedDescription(false), |
391 | inLegaleseText(false), |
392 | inLink(false), |
393 | inObsoleteLink(false), |
394 | inSectionHeading(false), |
395 | inTableHeader(false), |
396 | inTableBody(false), |
397 | noLinks(false), |
398 | obsoleteLinks(false), |
399 | offlineDocs(true), |
400 | threeColumnEnumValueTable(true), |
401 | codeIndent(0), |
402 | numTableRows(0), |
403 | divNestingLevel(0), |
404 | sectionNestingLevel(0), |
405 | tableColumnCount(0), |
406 | funcLeftParen("\\S(\\()"), |
407 | myTree(0) |
408 | { |
409 | // nothing yet. |
410 | } |
411 | |
412 | /*! |
413 | The destructor has nothing to do. |
414 | */ |
415 | DitaXmlGenerator::~DitaXmlGenerator() |
416 | { |
417 | GuidMaps::iterator i = guidMaps.begin(); |
418 | while (i != guidMaps.end()) { |
419 | delete i.value(); |
420 | ++i; |
421 | } |
422 | } |
423 | |
424 | /*! |
425 | A lot of internal structures are initialized. |
426 | */ |
427 | void DitaXmlGenerator::initializeGenerator(const Config &config) |
428 | { |
429 | Generator::initializeGenerator(config); |
430 | obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS)); |
431 | setImageFileExtensions(QStringList() << "png"<< "jpg"<< "jpeg"<< "gif"); |
432 | |
433 | style = config.getString(DitaXmlGenerator::format() + |
434 | Config::dot + |
435 | DITAXMLGENERATOR_STYLE); |
436 | postHeader = config.getString(DitaXmlGenerator::format() + |
437 | Config::dot + |
438 | DITAXMLGENERATOR_POSTHEADER); |
439 | postPostHeader = config.getString(DitaXmlGenerator::format() + |
440 | Config::dot + |
441 | DITAXMLGENERATOR_POSTPOSTHEADER); |
442 | footer = config.getString(DitaXmlGenerator::format() + |
443 | Config::dot + |
444 | DITAXMLGENERATOR_FOOTER); |
445 | address = config.getString(DitaXmlGenerator::format() + |
446 | Config::dot + |
447 | DITAXMLGENERATOR_ADDRESS); |
448 | pleaseGenerateMacRef = config.getBool(DitaXmlGenerator::format() + |
449 | Config::dot + |
450 | DITAXMLGENERATOR_GENERATEMACREFS); |
451 | |
452 | project = config.getString(CONFIG_PROJECT); |
453 | projectDescription = config.getString(CONFIG_DESCRIPTION); |
454 | if (projectDescription.isEmpty() && !project.isEmpty()) |
455 | projectDescription = project + " Reference Documentation"; |
456 | |
457 | projectUrl = config.getString(CONFIG_URL); |
458 | |
459 | outputEncoding = config.getString(CONFIG_OUTPUTENCODING); |
460 | if (outputEncoding.isEmpty()) |
461 | outputEncoding = QLatin1String("ISO-8859-1"); |
462 | outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit()); |
463 | |
464 | naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE); |
465 | if (naturalLanguage.isEmpty()) |
466 | naturalLanguage = QLatin1String("en"); |
467 | |
468 | config.subVarsAndValues("dita.metadata.default",metadataDefaults); |
469 | QSet<QString> editionNames = config.subVars(CONFIG_EDITION); |
470 | QSet<QString>::ConstIterator edition = editionNames.begin(); |
471 | while (edition != editionNames.end()) { |
472 | QString editionName = *edition; |
473 | QStringList editionModules = config.getStringList(CONFIG_EDITION + |
474 | Config::dot + |
475 | editionName + |
476 | Config::dot + |
477 | "modules"); |
478 | QStringList editionGroups = config.getStringList(CONFIG_EDITION + |
479 | Config::dot + |
480 | editionName + |
481 | Config::dot + |
482 | "groups"); |
483 | |
484 | if (!editionModules.isEmpty()) |
485 | editionModuleMap[editionName] = editionModules; |
486 | if (!editionGroups.isEmpty()) |
487 | editionGroupMap[editionName] = editionGroups; |
488 | |
489 | ++edition; |
490 | } |
491 | |
492 | stylesheets = config.getStringList(DitaXmlGenerator::format() + |
493 | Config::dot + |
494 | DITAXMLGENERATOR_STYLESHEETS); |
495 | customHeadElements = config.getStringList(DitaXmlGenerator::format() + |
496 | Config::dot + |
497 | DITAXMLGENERATOR_CUSTOMHEADELEMENTS); |
498 | codeIndent = config.getInt(CONFIG_CODEINDENT); |
499 | version = config.getString(CONFIG_VERSION); |
500 | vrm = version.split("."); |
501 | } |
502 | |
503 | /*! |
504 | All this does is call the same function in the base class. |
505 | */ |
506 | void DitaXmlGenerator::terminateGenerator() |
507 | { |
508 | Generator::terminateGenerator(); |
509 | } |
510 | |
511 | /*! |
512 | Returns "DITAXML". |
513 | */ |
514 | QString DitaXmlGenerator::format() |
515 | { |
516 | return "DITAXML"; |
517 | } |
518 | |
519 | /*! |
520 | Calls lookupGuid() to get a GUID for \a text, then writes |
521 | it to the XML stream as an "id" attribute, and returns it. |
522 | */ |
523 | QString DitaXmlGenerator::writeGuidAttribute(QString text) |
524 | { |
525 | QString guid = lookupGuid(outFileName(),text); |
526 | xmlWriter().writeAttribute("id",guid); |
527 | return guid; |
528 | } |
529 | |
530 | |
531 | /*! |
532 | Write's the GUID for the \a node to the current XML stream |
533 | as an "id" attribute. If the \a node doesn't yet have a GUID, |
534 | one is generated. |
535 | */ |
536 | void DitaXmlGenerator::writeGuidAttribute(Node* node) |
537 | { |
538 | xmlWriter().writeAttribute("id",node->guid()); |
539 | } |
540 | |
541 | /*! |
542 | Looks up \a text in the GUID map. If it finds \a text, |
543 | it returns the associated GUID. Otherwise it inserts |
544 | \a text into the map with a new GUID, and it returns |
545 | the new GUID. |
546 | */ |
547 | QString DitaXmlGenerator::lookupGuid(QString text) |
548 | { |
549 | QMap<QString, QString>::const_iterator i = name2guidMap.find(text); |
550 | if (i != name2guidMap.end()) |
551 | return i.value(); |
552 | QString t = QUuid::createUuid().toString(); |
553 | QString guid = "id-"+ t.mid(1,t.length()-2); |
554 | name2guidMap.insert(text,guid); |
555 | return guid; |
556 | } |
557 | |
558 | /*! |
559 | First, look up the GUID map for \a fileName. If there isn't |
560 | a GUID map for \a fileName, create one and insert it into |
561 | the map of GUID maps. Then look up \a text in that GUID map. |
562 | If \a text is found, return the associated GUID. Otherwise, |
563 | insert \a text into the GUID map with a new GUID, and return |
564 | the new GUID. |
565 | */ |
566 | QString DitaXmlGenerator::lookupGuid(const QString& fileName, const QString& text) |
567 | { |
568 | GuidMap* gm = lookupGuidMap(fileName); |
569 | GuidMap::const_iterator i = gm->find(text); |
570 | if (i != gm->end()) |
571 | return i.value(); |
572 | QString t = QUuid::createUuid().toString(); |
573 | QString guid = "id-"+ t.mid(1,t.length()-2); |
574 | gm->insert(text,guid); |
575 | return guid; |
576 | } |
577 | |
578 | /*! |
579 | Looks up \a fileName in the map of GUID maps. If it finds |
580 | \a fileName, it returns a pointer to the associated GUID |
581 | map. Otherwise it creates a new GUID map and inserts it |
582 | into the map of GUID maps with \a fileName as its key. |
583 | */ |
584 | GuidMap* DitaXmlGenerator::lookupGuidMap(const QString& fileName) |
585 | { |
586 | GuidMaps::const_iterator i = guidMaps.find(fileName); |
587 | if (i != guidMaps.end()) |
588 | return i.value(); |
589 | GuidMap* gm = new GuidMap; |
590 | guidMaps.insert(fileName,gm); |
591 | return gm; |
592 | } |
593 | |
594 | /*! |
595 | This is where the DITA XML files are written. |
596 | \note The file generation is done in the base class, |
597 | PageGenerator::generateTree(). |
598 | */ |
599 | void DitaXmlGenerator::generateTree(const Tree *tree) |
600 | { |
601 | myTree = tree; |
602 | nonCompatClasses.clear(); |
603 | mainClasses.clear(); |
604 | compatClasses.clear(); |
605 | obsoleteClasses.clear(); |
606 | moduleClassMap.clear(); |
607 | moduleNamespaceMap.clear(); |
608 | funcIndex.clear(); |
609 | legaleseTexts.clear(); |
610 | serviceClasses.clear(); |
611 | qmlClasses.clear(); |
612 | findAllClasses(tree->root()); |
613 | findAllFunctions(tree->root()); |
614 | findAllLegaleseTexts(tree->root()); |
615 | findAllNamespaces(tree->root()); |
616 | findAllSince(tree->root()); |
617 | |
618 | PageGenerator::generateTree(tree); |
619 | writeDitaMap(); |
620 | } |
621 | |
622 | void DitaXmlGenerator::startText(const Node* /* relative */, |
623 | CodeMarker* /* marker */) |
624 | { |
625 | inLink = false; |
626 | inContents = false; |
627 | inSectionHeading = false; |
628 | inTableHeader = false; |
629 | numTableRows = 0; |
630 | threeColumnEnumValueTable = true; |
631 | link.clear(); |
632 | sectionNumber.clear(); |
633 | } |
634 | |
635 | static int countTableColumns(const Atom* t) |
636 | { |
637 | int result = 0; |
638 | if (t->type() == Atom::TableHeaderLeft) { |
639 | while (t->type() == Atom::TableHeaderLeft) { |
640 | int count = 0; |
641 | t = t->next(); |
642 | while (t->type() != Atom::TableHeaderRight) { |
643 | if (t->type() == Atom::TableItemLeft) |
644 | ++count; |
645 | t = t->next(); |
646 | } |
647 | if (count > result) |
648 | result = count; |
649 | t = t->next(); |
650 | } |
651 | } |
652 | else if (t->type() == Atom::TableRowLeft) { |
653 | while (t->type() != Atom::TableRowRight) { |
654 | if (t->type() == Atom::TableItemLeft) |
655 | ++result; |
656 | t = t->next(); |
657 | } |
658 | } |
659 | return result; |
660 | } |
661 | |
662 | /*! |
663 | Generate html from an instance of Atom. |
664 | */ |
665 | int DitaXmlGenerator::generateAtom(const Atom *atom, |
666 | const Node *relative, |
667 | CodeMarker *marker) |
668 | { |
669 | int skipAhead = 0; |
670 | QString hx, str; |
671 | static bool in_para = false; |
672 | QString guid, hc, attr; |
673 | |
674 | switch (atom->type()) { |
675 | case Atom::AbstractLeft: |
676 | break; |
677 | case Atom::AbstractRight: |
678 | break; |
679 | case Atom::AutoLink: |
680 | if (!noLinks && !inLink && !inContents && !inSectionHeading) { |
681 | const Node* node = 0; |
682 | QString link = getLink(atom, relative, marker, &node); |
683 | if (!link.isEmpty()) { |
684 | beginLink(link); |
685 | generateLink(atom, relative, marker); |
686 | endLink(); |
687 | } |
688 | else { |
689 | writeCharacters(protectEnc(atom->string())); |
690 | } |
691 | } |
692 | else { |
693 | writeCharacters(protectEnc(atom->string())); |
694 | } |
695 | break; |
696 | case Atom::BaseName: |
697 | break; |
698 | case Atom::BriefLeft: |
699 | //if (relative->type() == Node::Fake) { |
700 | //skipAhead = skipAtoms(atom, Atom::BriefRight); |
701 | //break; |
702 | //} |
703 | if (inSection()) { |
704 | writeStartTag(DT_p); |
705 | xmlWriter().writeAttribute("outputclass", "brief"); |
706 | } |
707 | else { |
708 | noLinks = true; |
709 | writeStartTag(DT_shortdesc); |
710 | } |
711 | if (relative->type() == Node::Property || |
712 | relative->type() == Node::Variable) { |
713 | xmlWriter().writeCharacters("This "); |
714 | if (relative->type() == Node::Property) |
715 | xmlWriter().writeCharacters("property"); |
716 | else if (relative->type() == Node::Variable) |
717 | xmlWriter().writeCharacters("variable"); |
718 | xmlWriter().writeCharacters(" holds "); |
719 | } |
720 | if (noLinks) { |
721 | atom = atom->next(); |
722 | while (atom != 0 && atom->type() != Atom::BriefRight) { |
723 | if (atom->type() == Atom::String || |
724 | atom->type() == Atom::AutoLink) |
725 | str += atom->string(); |
726 | skipAhead++; |
727 | atom = atom->next(); |
728 | } |
729 | str[0] = str[0].toLower(); |
730 | if (str.right(1) == ".") |
731 | str.truncate(str.length() - 1); |
732 | writeCharacters(str + "."); |
733 | } |
734 | break; |
735 | case Atom::BriefRight: |
736 | // if (relative->type() != Node::Fake) |
737 | writeEndTag(); // </shortdesc> or </p> |
738 | noLinks = false; |
739 | break; |
740 | case Atom::C: |
741 | writeStartTag(DT_tt); |
742 | if (inLink) { |
743 | writeCharacters(protectEnc(plainCode(atom->string()))); |
744 | } |
745 | else { |
746 | writeText(atom->string(), marker, relative); |
747 | } |
748 | writeEndTag(); // see writeStartElement() above |
749 | break; |
750 | case Atom::Code: |
751 | { |
752 | writeStartTag(DT_codeblock); |
753 | xmlWriter().writeAttribute("outputclass", "cpp"); |
754 | QString chars = trimmedTrailing(atom->string()); |
755 | writeText(chars, marker, relative); |
756 | writeEndTag(); // </codeblock> |
757 | } |
758 | break; |
759 | case Atom::Qml: |
760 | writeStartTag(DT_codeblock); |
761 | xmlWriter().writeAttribute("outputclass", "qml"); |
762 | writeText(trimmedTrailing(atom->string()), marker, relative); |
763 | writeEndTag(); // </codeblock> |
764 | break; |
765 | case Atom::CodeNew: |
766 | writeStartTag(DT_p); |
767 | xmlWriter().writeCharacters("you can rewrite it as"); |
768 | writeEndTag(); // </p> |
769 | writeStartTag(DT_codeblock); |
770 | writeText(trimmedTrailing(atom->string()), marker, relative); |
771 | writeEndTag(); // </codeblock> |
772 | break; |
773 | case Atom::CodeOld: |
774 | writeStartTag(DT_p); |
775 | xmlWriter().writeCharacters("For example, if you have code like"); |
776 | writeEndTag(); // </p> |
777 | // fallthrough |
778 | case Atom::CodeBad: |
779 | writeStartTag(DT_codeblock); |
780 | writeCharacters(trimmedTrailing(plainCode(atom->string()))); |
781 | writeEndTag(); // </codeblock> |
782 | break; |
783 | case Atom::DivLeft: |
784 | { |
785 | bool inStartElement = false; |
786 | attr = atom->string(); |
787 | DitaTag t = currentTag(); |
788 | if ((t == DT_section) || (t == DT_sectiondiv)) { |
789 | writeStartTag(DT_sectiondiv); |
790 | divNestingLevel++; |
791 | inStartElement = true; |
792 | } |
793 | else if ((t == DT_body) || (t == DT_bodydiv)) { |
794 | writeStartTag(DT_bodydiv); |
795 | divNestingLevel++; |
796 | inStartElement = true; |
797 | } |
798 | if (!attr.isEmpty()) { |
799 | if (attr.contains( |
800 | int index = 0; |
801 | int from = 0; |
802 | QString values; |
803 | while (index >= 0) { |
804 | index = attr.indexOf( |
805 | if (index >= 0) { |
806 | ++index; |
807 | from = index; |
808 | index = attr.indexOf( |
809 | if (index > from) { |
810 | if (!values.isEmpty()) |
811 | values.append( |
812 | values += attr.mid(from,index-from); |
813 | from = index+1; |
814 | } |
815 | } |
816 | } |
817 | attr = values; |
818 | } |
819 | } |
820 | if (inStartElement) |
821 | xmlWriter().writeAttribute("outputclass", attr); |
822 | } |
823 | break; |
824 | case Atom::DivRight: |
825 | if ((currentTag() == DT_sectiondiv) || (currentTag() == DT_bodydiv)) { |
826 | writeEndTag(); // </sectiondiv>, </bodydiv>, or </p> |
827 | if (divNestingLevel > 0) |
828 | --divNestingLevel; |
829 | } |
830 | break; |
831 | case Atom::FootnoteLeft: |
832 | // ### For now |
833 | if (in_para) { |
834 | writeEndTag(); // </p> |
835 | in_para = false; |
836 | } |
837 | xmlWriter().writeCharacters("<!-- "); |
838 | break; |
839 | case Atom::FootnoteRight: |
840 | // ### For now |
841 | xmlWriter().writeCharacters("-->"); |
842 | break; |
843 | case Atom::FormatElse: |
844 | case Atom::FormatEndif: |
845 | case Atom::FormatIf: |
846 | break; |
847 | case Atom::FormattingLeft: |
848 | { |
849 | DitaTag t = DT_LAST; |
850 | if (atom->string() == ATOM_FORMATTING_BOLD) |
851 | t = DT_b; |
852 | else if (atom->string() == ATOM_FORMATTING_PARAMETER) |
853 | t = DT_i; |
854 | else if (atom->string() == ATOM_FORMATTING_ITALIC) |
855 | t = DT_i; |
856 | else if (atom->string() == ATOM_FORMATTING_TELETYPE) |
857 | t = DT_tt; |
858 | else if (atom->string().startsWith("span ")) { |
859 | t = DT_keyword; |
860 | } |
861 | else if (atom->string() == ATOM_FORMATTING_UNDERLINE) |
862 | t = DT_u; |
863 | else if (atom->string() == ATOM_FORMATTING_INDEX) |
864 | t = DT_comment; |
865 | else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT) |
866 | t = DT_sub; |
867 | else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT) |
868 | t = DT_sup; |
869 | else |
870 | qDebug() << "DT_LAST"; |
871 | writeStartTag(t); |
872 | if (atom->string() == ATOM_FORMATTING_PARAMETER) { |
873 | if (atom->next() != 0 && atom->next()->type() == Atom::String) { |
874 | QRegExp subscriptRegExp("([a-z]+)_([0-9n])"); |
875 | if (subscriptRegExp.exactMatch(atom->next()->string())) { |
876 | xmlWriter().writeCharacters(subscriptRegExp.cap(1)); |
877 | writeStartTag(DT_sub); |
878 | xmlWriter().writeCharacters(subscriptRegExp.cap(2)); |
879 | writeEndTag(); // </sub> |
880 | skipAhead = 1; |
881 | } |
882 | } |
883 | } |
884 | else if (t == DT_keyword) { |
885 | QString attr = atom->string().mid(5); |
886 | if (!attr.isEmpty()) { |
887 | if (attr.contains( |
888 | int index = 0; |
889 | int from = 0; |
890 | QString values; |
891 | while (index >= 0) { |
892 | index = attr.indexOf( |
893 | if (index >= 0) { |
894 | ++index; |
895 | from = index; |
896 | index = attr.indexOf( |
897 | if (index > from) { |
898 | if (!values.isEmpty()) |
899 | values.append( |
900 | values += attr.mid(from,index-from); |
901 | from = index+1; |
902 | } |
903 | } |
904 | } |
905 | attr = values; |
906 | } |
907 | } |
908 | xmlWriter().writeAttribute("outputclass", attr); |
909 | } |
910 | } |
911 | break; |
912 | case Atom::FormattingRight: |
913 | if (atom->string() == ATOM_FORMATTING_LINK) { |
914 | endLink(); |
915 | } |
916 | else { |
917 | writeEndTag(); // ? |
918 | } |
919 | break; |
920 | case Atom::AnnotatedList: |
921 | { |
922 | QList<Node*> values = myTree->groups().values(atom->string()); |
923 | NodeMap nodeMap; |
924 | for (int i = 0; i < values.size(); ++i) { |
925 | const Node* n = values.at(i); |
926 | if ((n->status() != Node::Internal) && (n->access() != Node::Private)) { |
927 | nodeMap.insert(n->nameForLists(),n); |
928 | } |
929 | } |
930 | generateAnnotatedList(relative, marker, nodeMap); |
931 | } |
932 | break; |
933 | case Atom::GeneratedList: |
934 | if (atom->string() == "annotatedclasses") { |
935 | generateAnnotatedList(relative, marker, nonCompatClasses); |
936 | } |
937 | else if (atom->string() == "classes") { |
938 | generateCompactList(relative, marker, nonCompatClasses, true); |
939 | } |
940 | else if (atom->string() == "qmlclasses") { |
941 | generateCompactList(relative, marker, qmlClasses, true); |
942 | } |
943 | else if (atom->string().contains("classesbymodule")) { |
944 | QString arg = atom->string().trimmed(); |
945 | QString moduleName = atom->string().mid(atom->string().indexOf( |
946 | "classesbymodule") + 15).trimmed(); |
947 | if (moduleClassMap.contains(moduleName)) |
948 | generateAnnotatedList(relative, marker, moduleClassMap[moduleName]); |
949 | } |
950 | else if (atom->string().contains("classesbyedition")) { |
951 | |
952 | QString arg = atom->string().trimmed(); |
953 | QString editionName = atom->string().mid(atom->string().indexOf( |
954 | "classesbyedition") + 16).trimmed(); |
955 | |
956 | if (editionModuleMap.contains(editionName)) { |
957 | |
958 | // Add all classes in the modules listed for that edition. |
959 | NodeMap editionClasses; |
960 | foreach (const QString &moduleName, editionModuleMap[editionName]) { |
961 | if (moduleClassMap.contains(moduleName)) |
962 | editionClasses.unite(moduleClassMap[moduleName]); |
963 | } |
964 | |
965 | // Add additional groups and remove groups of classes that |
966 | // should be excluded from the edition. |
967 | |
968 | QMultiMap <QString, Node *> groups = myTree->groups(); |
969 | foreach (const QString &groupName, editionGroupMap[editionName]) { |
970 | QList<Node *> groupClasses; |
971 | if (groupName.startsWith("-")) { |
972 | groupClasses = groups.values(groupName.mid(1)); |
973 | foreach (const Node *node, groupClasses) |
974 | editionClasses.remove(node->name()); |
975 | } |
976 | else { |
977 | groupClasses = groups.values(groupName); |
978 | foreach (const Node *node, groupClasses) |
979 | editionClasses.insert(node->name(), node); |
980 | } |
981 | } |
982 | generateAnnotatedList(relative, marker, editionClasses); |
983 | } |
984 | } |
985 | else if (atom->string() == "classhierarchy") { |
986 | generateClassHierarchy(relative, marker, nonCompatClasses); |
987 | } |
988 | else if (atom->string() == "compatclasses") { |
989 | generateCompactList(relative, marker, compatClasses, false); |
990 | } |
991 | else if (atom->string() == "obsoleteclasses") { |
992 | generateCompactList(relative, marker, obsoleteClasses, false); |
993 | } |
994 | else if (atom->string() == "functionindex") { |
995 | generateFunctionIndex(relative, marker); |
996 | } |
997 | else if (atom->string() == "legalese") { |
998 | generateLegaleseList(relative, marker); |
999 | } |
1000 | else if (atom->string() == "mainclasses") { |
1001 | generateCompactList(relative, marker, mainClasses, true); |
1002 | } |
1003 | else if (atom->string() == "services") { |
1004 | generateCompactList(relative, marker, serviceClasses, false); |
1005 | } |
1006 | else if (atom->string() == "overviews") { |
1007 | generateOverviewList(relative, marker); |
1008 | } |
1009 | else if (atom->string() == "namespaces") { |
1010 | generateAnnotatedList(relative, marker, namespaceIndex); |
1011 | } |
1012 | else if (atom->string() == "related") { |
1013 | const FakeNode *fake = static_cast<const FakeNode *>(relative); |
1014 | if (fake && !fake->groupMembers().isEmpty()) { |
1015 | NodeMap groupMembersMap; |
1016 | foreach (const Node *node, fake->groupMembers()) { |
1017 | if (node->type() == Node::Fake) |
1018 | groupMembersMap[fullName(node, relative, marker)] = node; |
1019 | } |
1020 | generateAnnotatedList(fake, marker, groupMembersMap); |
1021 | } |
1022 | } |
1023 | break; |
1024 | case Atom::SinceList: |
1025 | { |
1026 | NewSinceMaps::const_iterator nsmap; |
1027 | nsmap = newSinceMaps.find(atom->string()); |
1028 | NewClassMaps::const_iterator ncmap; |
1029 | ncmap = newClassMaps.find(atom->string()); |
1030 | NewClassMaps::const_iterator nqcmap; |
1031 | nqcmap = newQmlClassMaps.find(atom->string()); |
1032 | if ((nsmap != newSinceMaps.constEnd()) && !nsmap.value().isEmpty()) { |
1033 | QList<Section> sections; |
1034 | QList<Section>::ConstIterator s; |
1035 | for (int i=0; i<LastSinceType; ++i) |
1036 | sections.append(Section(sinceTitle(i),QString(),QString(),QString())); |
1037 | |
1038 | NodeMultiMap::const_iterator n = nsmap.value().constBegin(); |
1039 | while (n != nsmap.value().constEnd()) { |
1040 | const Node* node = n.value(); |
1041 | switch (node->type()) { |
1042 | case Node::Fake: |
1043 | if (node->subType() == Node::QmlClass) { |
1044 | sections[QmlClass].appendMember((Node*)node); |
1045 | } |
1046 | break; |
1047 | case Node::Namespace: |
1048 | sections[Namespace].appendMember((Node*)node); |
1049 | break; |
1050 | case Node::Class: |
1051 | sections[Class].appendMember((Node*)node); |
1052 | break; |
1053 | case Node::Enum: |
1054 | sections[Enum].appendMember((Node*)node); |
1055 | break; |
1056 | case Node::Typedef: |
1057 | sections[Typedef].appendMember((Node*)node); |
1058 | break; |
1059 | case Node::Function: { |
1060 | const FunctionNode* fn = static_cast<const FunctionNode*>(node); |
1061 | if (fn->isMacro()) |
1062 | sections[Macro].appendMember((Node*)node); |
1063 | else { |
1064 | Node* p = fn->parent(); |
1065 | if (p) { |
1066 | if (p->type() == Node::Class) |
1067 | sections[MemberFunction].appendMember((Node*)node); |
1068 | else if (p->type() == Node::Namespace) { |
1069 | if (p->name().isEmpty()) |
1070 | sections[GlobalFunction].appendMember((Node*)node); |
1071 | else |
1072 | sections[NamespaceFunction].appendMember((Node*)node); |
1073 | } |
1074 | else |
1075 | sections[GlobalFunction].appendMember((Node*)node); |
1076 | } |
1077 | else |
1078 | sections[GlobalFunction].appendMember((Node*)node); |
1079 | } |
1080 | break; |
1081 | } |
1082 | case Node::Property: |
1083 | sections[Property].appendMember((Node*)node); |
1084 | break; |
1085 | case Node::Variable: |
1086 | sections[Variable].appendMember((Node*)node); |
1087 | break; |
1088 | case Node::QmlProperty: |
1089 | sections[QmlProperty].appendMember((Node*)node); |
1090 | break; |
1091 | case Node::QmlSignal: |
1092 | sections[QmlSignal].appendMember((Node*)node); |
1093 | break; |
1094 | case Node::QmlMethod: |
1095 | sections[QmlMethod].appendMember((Node*)node); |
1096 | break; |
1097 | default: |
1098 | break; |
1099 | } |
1100 | ++n; |
1101 | } |
1102 | |
1103 | /* |
1104 | First generate the table of contents. |
1105 | */ |
1106 | writeStartTag(DT_ul); |
1107 | s = sections.constBegin(); |
1108 | while (s != sections.constEnd()) { |
1109 | if (!(*s).members.isEmpty()) { |
1110 | QString li = outFileName() + "#"+ Doc::canonicalTitle((*s).name); |
1111 | writeXrefListItem(li, (*s).name); |
1112 | } |
1113 | ++s; |
1114 | } |
1115 | writeEndTag(); // </ul> |
1116 | |
1117 | int idx = 0; |
1118 | s = sections.constBegin(); |
1119 | while (s != sections.constEnd()) { |
1120 | if (!(*s).members.isEmpty()) { |
1121 | writeStartTag(DT_p); |
1122 | writeGuidAttribute(Doc::canonicalTitle((*s).name)); |
1123 | xmlWriter().writeAttribute("outputclass", "h3"); |
1124 | writeCharacters(protectEnc((*s).name)); |
1125 | writeEndTag(); // </p> |
1126 | if (idx == Class) |
1127 | generateCompactList(0, marker, ncmap.value(), false, QString("Q")); |
1128 | else if (idx == QmlClass) |
1129 | generateCompactList(0, marker, nqcmap.value(), false, QString("Q")); |
1130 | else if (idx == MemberFunction) { |
1131 | ParentMaps parentmaps; |
1132 | ParentMaps::iterator pmap; |
1133 | NodeList::const_iterator i = s->members.constBegin(); |
1134 | while (i != s->members.constEnd()) { |
1135 | Node* p = (*i)->parent(); |
1136 | pmap = parentmaps.find(p); |
1137 | if (pmap == parentmaps.end()) |
1138 | pmap = parentmaps.insert(p,NodeMultiMap()); |
1139 | pmap->insert((*i)->name(),(*i)); |
1140 | ++i; |
1141 | } |
1142 | pmap = parentmaps.begin(); |
1143 | while (pmap != parentmaps.end()) { |
1144 | NodeList nlist = pmap->values(); |
1145 | writeStartTag(DT_p); |
1146 | xmlWriter().writeCharacters("Class "); |
1147 | writeStartTag(DT_xref); |
1148 | // formathtml |
1149 | xmlWriter().writeAttribute("href",linkForNode(pmap.key(), 0)); |
1150 | QStringList pieces = fullName(pmap.key(), 0, marker).split("::"); |
1151 | writeCharacters(protectEnc(pieces.last())); |
1152 | writeEndTag(); // </xref> |
1153 | xmlWriter().writeCharacters(":"); |
1154 | writeEndTag(); // </p> |
1155 | |
1156 | generateSection(nlist, 0, marker, CodeMarker::Summary); |
1157 | ++pmap; |
1158 | } |
1159 | } |
1160 | else { |
1161 | generateSection(s->members, 0, marker, CodeMarker::Summary); |
1162 | } |
1163 | } |
1164 | ++idx; |
1165 | ++s; |
1166 | } |
1167 | } |
1168 | } |
1169 | break; |
1170 | case Atom::Image: |
1171 | case Atom::InlineImage: |
1172 | { |
1173 | QString fileName = imageFileName(relative, atom->string()); |
1174 | QString text; |
1175 | if (atom->next() != 0) |
1176 | text = atom->next()->string(); |
1177 | if (fileName.isEmpty()) { |
1178 | /* |
1179 | Don't bother outputting an error message. |
1180 | Just output the href as if the image is in |
1181 | the images directory... |
1182 | */ |
1183 | if (atom->string()[0] == |
1184 | fileName = QLatin1String("images") + atom->string(); |
1185 | else |
1186 | fileName = QLatin1String("images/") + atom->string(); |
1187 | } |
1188 | |
1189 | if (currentTag() != DT_xref) |
1190 | writeStartTag(DT_fig); |
1191 | writeStartTag(DT_image); |
1192 | xmlWriter().writeAttribute("href",protectEnc(fileName)); |
1193 | if (atom->type() == Atom::InlineImage) |
1194 | xmlWriter().writeAttribute("placement", "inline"); |
1195 | else { |
1196 | xmlWriter().writeAttribute("placement", "break"); |
1197 | xmlWriter().writeAttribute("align", "center"); |
1198 | } |
1199 | if (!text.isEmpty()) { |
1200 | writeStartTag(DT_alt); |
1201 | writeCharacters(protectEnc(text)); |
1202 | writeEndTag(); // </alt> |
1203 | } |
1204 | writeEndTag(); // </image> |
1205 | if (currentTag() != DT_xref) |
1206 | writeEndTag(); // </fig> |
1207 | } |
1208 | break; |
1209 | case Atom::ImageText: |
1210 | // nothing |
1211 | break; |
1212 | case Atom::LegaleseLeft: |
1213 | inLegaleseText = true; |
1214 | break; |
1215 | case Atom::LegaleseRight: |
1216 | inLegaleseText = false; |
1217 | break; |
1218 | case Atom::LineBreak: |
1219 | //xmlWriter().writeEmptyElement("br"); |
1220 | break; |
1221 | case Atom::Link: |
1222 | { |
1223 | const Node *node = 0; |
1224 | QString myLink = getLink(atom, relative, marker, &node); |
1225 | if (myLink.isEmpty()) { |
1226 | relative->doc().location().warning(tr("Can't link to '%1' in %2") |
1227 | .arg(atom->string()) |
1228 | .arg(marker->plainFullName(relative))); |
1229 | } |
1230 | else if (!inSectionHeading) { |
1231 | beginLink(myLink); |
1232 | } |
1233 | #if 0 |
1234 | else { |
1235 | //xmlWriter().writeCharacters(atom->string()); |
1236 | //qDebug() << "MYLINK:" << myLink << outFileName() << atom->string(); |
1237 | } |
1238 | #endif |
1239 | skipAhead = 1; |
1240 | } |
1241 | break; |
1242 | case Atom::GuidLink: |
1243 | { |
1244 | beginLink(atom->string()); |
1245 | skipAhead = 1; |
1246 | } |
1247 | break; |
1248 | case Atom::LinkNode: |
1249 | { |
1250 | const Node* node = CodeMarker::nodeForString(atom->string()); |
1251 | beginLink(linkForNode(node, relative)); |
1252 | skipAhead = 1; |
1253 | } |
1254 | break; |
1255 | case Atom::ListLeft: |
1256 | if (in_para) { |
1257 | writeEndTag(); // </p> |
1258 | in_para = false; |
1259 | } |
1260 | if (atom->string() == ATOM_LIST_BULLET) { |
1261 | writeStartTag(DT_ul); |
1262 | } |
1263 | else if (atom->string() == ATOM_LIST_TAG) { |
1264 | writeStartTag(DT_dl); |
1265 | } |
1266 | else if (atom->string() == ATOM_LIST_VALUE) { |
1267 | threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom); |
1268 | if (threeColumnEnumValueTable) { |
1269 | writeStartTag(DT_simpletable); |
1270 | xmlWriter().writeAttribute("outputclass", "valuelist"); |
1271 | writeStartTag(DT_sthead); |
1272 | writeStartTag(DT_stentry); |
1273 | xmlWriter().writeCharacters("Constant"); |
1274 | writeEndTag(); // </stentry> |
1275 | writeStartTag(DT_stentry); |
1276 | xmlWriter().writeCharacters("Value"); |
1277 | writeEndTag(); // </stentry> |
1278 | writeStartTag(DT_stentry); |
1279 | xmlWriter().writeCharacters("Description"); |
1280 | writeEndTag(); // </stentry> |
1281 | writeEndTag(); // </sthead> |
1282 | } |
1283 | else { |
1284 | writeStartTag(DT_simpletable); |
1285 | xmlWriter().writeAttribute("outputclass", "valuelist"); |
1286 | writeStartTag(DT_sthead); |
1287 | writeStartTag(DT_stentry); |
1288 | xmlWriter().writeCharacters("Constant"); |
1289 | writeEndTag(); // </stentry> |
1290 | writeStartTag(DT_stentry); |
1291 | xmlWriter().writeCharacters("Value"); |
1292 | writeEndTag(); // </stentry> |
1293 | writeEndTag(); // </sthead> |
1294 | } |
1295 | } |
1296 | else { |
1297 | writeStartTag(DT_ol); |
1298 | if (atom->string() == ATOM_LIST_UPPERALPHA) |
1299 | xmlWriter().writeAttribute("outputclass", "upperalpha"); |
1300 | else if (atom->string() == ATOM_LIST_LOWERALPHA) |
1301 | xmlWriter().writeAttribute("outputclass", "loweralpha"); |
1302 | else if (atom->string() == ATOM_LIST_UPPERROMAN) |
1303 | xmlWriter().writeAttribute("outputclass", "upperroman"); |
1304 | else if (atom->string() == ATOM_LIST_LOWERROMAN) |
1305 | xmlWriter().writeAttribute("outputclass", "lowerroman"); |
1306 | else // (atom->string() == ATOM_LIST_NUMERIC) |
1307 | xmlWriter().writeAttribute("outputclass", "numeric"); |
1308 | if (atom->next() != 0 && atom->next()->string().toInt() != 1) { |
1309 | // I don't think this attribute is supported. |
1310 | xmlWriter().writeAttribute("start",atom->next()->string()); |
1311 | } |
1312 | } |
1313 | break; |
1314 | case Atom::ListItemNumber: |
1315 | // nothing |
1316 | break; |
1317 | case Atom::ListTagLeft: |
1318 | if (atom->string() == ATOM_LIST_TAG) { |
1319 | writeStartTag(DT_dt); |
1320 | } |
1321 | else { // (atom->string() == ATOM_LIST_VALUE) |
1322 | writeStartTag(DT_strow); |
1323 | writeStartTag(DT_stentry); |
1324 | writeStartTag(DT_tt); |
1325 | writeCharacters(protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(), |
1326 | relative)))); |
1327 | writeEndTag(); // </tt> |
1328 | writeEndTag(); // </stentry> |
1329 | writeStartTag(DT_stentry); |
1330 | |
1331 | QString itemValue; |
1332 | if (relative->type() == Node::Enum) { |
1333 | const EnumNode *enume = static_cast<const EnumNode *>(relative); |
1334 | itemValue = enume->itemValue(atom->next()->string()); |
1335 | } |
1336 | |
1337 | if (itemValue.isEmpty()) |
1338 | xmlWriter().writeCharacters("?"); |
1339 | else { |
1340 | writeStartTag(DT_tt); |
1341 | writeCharacters(protectEnc(itemValue)); |
1342 | writeEndTag(); // </tt> |
1343 | } |
1344 | skipAhead = 1; |
1345 | } |
1346 | break; |
1347 | case Atom::ListTagRight: |
1348 | if (atom->string() == ATOM_LIST_TAG) |
1349 | writeEndTag(); // </dt> |
1350 | break; |
1351 | case Atom::ListItemLeft: |
1352 | if (atom->string() == ATOM_LIST_TAG) { |
1353 | writeStartTag(DT_dd); |
1354 | } |
1355 | else if (atom->string() == ATOM_LIST_VALUE) { |
1356 | if (threeColumnEnumValueTable) { |
1357 | writeEndTag(); // </stentry> |
1358 | writeStartTag(DT_stentry); |
1359 | } |
1360 | } |
1361 | else { |
1362 | writeStartTag(DT_li); |
1363 | } |
1364 | if (matchAhead(atom, Atom::ParaLeft)) |
1365 | skipAhead = 1; |
1366 | break; |
1367 | case Atom::ListItemRight: |
1368 | if (atom->string() == ATOM_LIST_TAG) { |
1369 | writeEndTag(); // </dd> |
1370 | } |
1371 | else if (atom->string() == ATOM_LIST_VALUE) { |
1372 | writeEndTag(); // </stentry> |
1373 | writeEndTag(); // </strow> |
1374 | } |
1375 | else { |
1376 | writeEndTag(); // </li> |
1377 | } |
1378 | break; |
1379 | case Atom::ListRight: |
1380 | if (atom->string() == ATOM_LIST_BULLET) { |
1381 | writeEndTag(); // </ul> |
1382 | } |
1383 | else if (atom->string() == ATOM_LIST_TAG) { |
1384 | writeEndTag(); // </dl> |
1385 | } |
1386 | else if (atom->string() == ATOM_LIST_VALUE) { |
1387 | writeEndTag(); // </simpletable> |
1388 | } |
1389 | else { |
1390 | writeEndTag(); // </ol> |
1391 | } |
1392 | break; |
1393 | case Atom::Nop: |
1394 | // nothing |
1395 | break; |
1396 | case Atom::ParaLeft: |
1397 | writeStartTag(DT_p); |
1398 | if (inLegaleseText) |
1399 | xmlWriter().writeAttribute("outputclass", "legalese"); |
1400 | in_para = true; |
1401 | break; |
1402 | case Atom::ParaRight: |
1403 | endLink(); |
1404 | if (in_para) { |
1405 | writeEndTag(); // </p> |
1406 | in_para = false; |
1407 | } |
1408 | break; |
1409 | case Atom::QuotationLeft: |
1410 | writeStartTag(DT_lq); |
1411 | break; |
1412 | case Atom::QuotationRight: |
1413 | writeEndTag(); // </lq> |
1414 | break; |
1415 | case Atom::RawString: |
1416 | if (atom->string() == " ") |
1417 | break; |
1418 | if (atom->string().startsWith("&")) |
1419 | writeCharacters(atom->string()); |
1420 | else if (atom->string() == "<sup>*</sup>") { |
1421 | writeStartTag(DT_sup); |
1422 | writeCharacters("*"); |
1423 | writeEndTag(); // </sup> |
1424 | } |
1425 | else if (atom->string() == "<sup>®</sup>") { |
1426 | writeStartTag(DT_tm); |
1427 | xmlWriter().writeAttribute("tmtype", "reg"); |
1428 | writeEndTag(); // </tm> |
1429 | } |
1430 | else { |
1431 | writeStartTag(DT_pre); |
1432 | xmlWriter().writeAttribute("outputclass", "raw-html"); |
1433 | writeCharacters(atom->string()); |
1434 | writeEndTag(); // </pre> |
1435 | } |
1436 | break; |
1437 | case Atom::SectionLeft: |
1438 | #if 0 |
1439 | if (inApiDesc) { |
1440 | writeEndTag(); // </apiDesc> |
1441 | inApiDesc = false; |
1442 | } |
1443 | #endif |
1444 | enterSection("details",QString()); |
1445 | //writeGuidAttribute(Doc::canonicalTitle(Text::sectionHeading(atom).toString())); |
1446 | break; |
1447 | case Atom::SectionRight: |
1448 | leaveSection(); |
1449 | break; |
1450 | case Atom::SectionHeadingLeft: |
1451 | writeStartTag(DT_p); |
1452 | writeGuidAttribute(Doc::canonicalTitle(Text::sectionHeading(atom).toString())); |
1453 | hx = "h"+ QString::number(atom->string().toInt() + hOffset(relative)); |
1454 | xmlWriter().writeAttribute("outputclass",hx); |
1455 | inSectionHeading = true; |
1456 | break; |
1457 | case Atom::SectionHeadingRight: |
1458 | writeEndTag(); // </title> (see case Atom::SectionHeadingLeft) |
1459 | inSectionHeading = false; |
1460 | break; |
1461 | case Atom::SidebarLeft: |
1462 | // nothing |
1463 | break; |
1464 | case Atom::SidebarRight: |
1465 | // nothing |
1466 | break; |
1467 | case Atom::String: |
1468 | if (inLink && !inContents && !inSectionHeading) { |
1469 | generateLink(atom, relative, marker); |
1470 | } |
1471 | else { |
1472 | writeCharacters(atom->string()); |
1473 | } |
1474 | break; |
1475 | case Atom::TableLeft: |
1476 | { |
1477 | if (in_para) { |
1478 | writeEndTag(); // </p> |
1479 | in_para = false; |
1480 | } |
1481 | writeStartTag(DT_table); |
1482 | numTableRows = 0; |
1483 | if (tableColumnCount != 0) { |
1484 | qDebug() << "ERROR: Nested tables!"; |
1485 | tableColumnCount = 0; |
1486 | } |
1487 | tableColumnCount = countTableColumns(atom->next()); |
1488 | writeStartTag(DT_tgroup); |
1489 | xmlWriter().writeAttribute("cols",QString::number(tableColumnCount)); |
1490 | inTableHeader = false; |
1491 | inTableBody = false; |
1492 | } |
1493 | break; |
1494 | case Atom::TableRight: |
1495 | writeEndTag(); // </tbody> |
1496 | writeEndTag(); // </tgroup> |
1497 | writeEndTag(); // </table> |
1498 | inTableHeader = false; |
1499 | inTableBody = false; |
1500 | tableColumnCount = 0; |
1501 | break; |
1502 | case Atom::TableHeaderLeft: |
1503 | if (inTableBody) { |
1504 | writeEndTag(); // </tbody> |
1505 | writeEndTag(); // </tgroup> |
1506 | writeEndTag(); // </table> |
1507 | inTableHeader = false; |
1508 | inTableBody = false; |
1509 | tableColumnCount = 0; |
1510 | writeStartTag(DT_table); |
1511 | numTableRows = 0; |
1512 | tableColumnCount = countTableColumns(atom); |
1513 | writeStartTag(DT_tgroup); |
1514 | xmlWriter().writeAttribute("cols",QString::number(tableColumnCount)); |
1515 | } |
1516 | writeStartTag(DT_thead); |
1517 | xmlWriter().writeAttribute("valign", "top"); |
1518 | writeStartTag(DT_row); |
1519 | xmlWriter().writeAttribute("valign", "top"); |
1520 | inTableHeader = true; |
1521 | inTableBody = false; |
1522 | break; |
1523 | case Atom::TableHeaderRight: |
1524 | writeEndTag(); // </row> |
1525 | if (matchAhead(atom, Atom::TableHeaderLeft)) { |
1526 | skipAhead = 1; |
1527 | writeStartTag(DT_row); |
1528 | xmlWriter().writeAttribute("valign", "top"); |
1529 | } |
1530 | else { |
1531 | writeEndTag(); // </thead> |
1532 | inTableHeader = false; |
1533 | inTableBody = true; |
1534 | writeStartTag(DT_tbody); |
1535 | } |
1536 | break; |
1537 | case Atom::TableRowLeft: |
1538 | if (!inTableHeader && !inTableBody) { |
1539 | inTableBody = true; |
1540 | writeStartTag(DT_tbody); |
1541 | } |
1542 | writeStartTag(DT_row); |
1543 | attr = atom->string(); |
1544 | if (!attr.isEmpty()) { |
1545 | if (attr.contains( |
1546 | int index = 0; |
1547 | int from = 0; |
1548 | QString values; |
1549 | while (index >= 0) { |
1550 | index = attr.indexOf( |
1551 | if (index >= 0) { |
1552 | ++index; |
1553 | from = index; |
1554 | index = attr.indexOf( |
1555 | if (index > from) { |
1556 | if (!values.isEmpty()) |
1557 | values.append( |
1558 | values += attr.mid(from,index-from); |
1559 | from = index+1; |
1560 | } |
1561 | } |
1562 | } |
1563 | attr = values; |
1564 | } |
1565 | xmlWriter().writeAttribute("outputclass", attr); |
1566 | } |
1567 | xmlWriter().writeAttribute("valign", "top"); |
1568 | break; |
1569 | case Atom::TableRowRight: |
1570 | writeEndTag(); // </row> |
1571 | break; |
1572 | case Atom::TableItemLeft: |
1573 | { |
1574 | QString values = ""; |
1575 | writeStartTag(DT_entry); |
1576 | for (int i=0; i<atom->count(); ++i) { |
1577 | attr = atom->string(i); |
1578 | if (attr.contains( |
1579 | int index = 0; |
1580 | int from = 0; |
1581 | while (index >= 0) { |
1582 | index = attr.indexOf( |
1583 | if (index >= 0) { |
1584 | ++index; |
1585 | from = index; |
1586 | index = attr.indexOf( |
1587 | if (index > from) { |
1588 | if (!values.isEmpty()) |
1589 | values.append( |
1590 | values += attr.mid(from,index-from); |
1591 | from = index+1; |
1592 | } |
1593 | } |
1594 | } |
1595 | } |
1596 | else { |
1597 | QStringList spans = attr.split(","); |
1598 | if (spans.size() == 2) { |
1599 | if ((spans[0].toInt()>1) || (spans[1].toInt()>1)) { |
1600 | values += "span("+ spans[0] + ","+ spans[1] + ")"; |
1601 | } |
1602 | } |
1603 | } |
1604 | } |
1605 | if (!values.isEmpty()) |
1606 | xmlWriter().writeAttribute("outputclass",values); |
1607 | if (matchAhead(atom, Atom::ParaLeft)) |
1608 | skipAhead = 1; |
1609 | } |
1610 | break; |
1611 | case Atom::TableItemRight: |
1612 | if (inTableHeader) |
1613 | writeEndTag(); // </entry> |
1614 | else { |
1615 | writeEndTag(); // </entry> |
1616 | } |
1617 | if (matchAhead(atom, Atom::ParaLeft)) |
1618 | skipAhead = 1; |
1619 | break; |
1620 | case Atom::TableOfContents: |
1621 | { |
1622 | int numColumns = 1; |
1623 | const Node* node = relative; |
1624 | |
1625 | Doc::Sections sectionUnit = Doc::Section4; |
1626 | QStringList params = atom->string().split(","); |
1627 | QString columnText = params.at(0); |
1628 | QStringList pieces = columnText.split(" ", QString::SkipEmptyParts); |
1629 | if (pieces.size() >= 2) { |
1630 | columnText = pieces.at(0); |
1631 | pieces.pop_front(); |
1632 | QString path = pieces.join(" ").trimmed(); |
1633 | node = findNodeForTarget(path, relative, marker, atom); |
1634 | } |
1635 | |
1636 | if (params.size() == 2) { |
1637 | numColumns = qMax(columnText.toInt(), numColumns); |
1638 | sectionUnit = (Doc::Sections)params.at(1).toInt(); |
1639 | } |
1640 | |
1641 | if (node) |
1642 | generateTableOfContents(node, |
1643 | marker, |
1644 | sectionUnit, |
1645 | numColumns, |
1646 | relative); |
1647 | } |
1648 | break; |
1649 | case Atom::Target: |
1650 | if (in_para) { |
1651 | writeEndTag(); // </p> |
1652 | in_para = false; |
1653 | } |
1654 | writeStartTag(DT_p); |
1655 | writeGuidAttribute(Doc::canonicalTitle(atom->string())); |
1656 | xmlWriter().writeAttribute("outputclass", "target"); |
1657 | //xmlWriter().writeCharacters(protectEnc(atom->string())); |
1658 | writeEndTag(); // </p> |
1659 | break; |
1660 | case Atom::UnhandledFormat: |
1661 | writeStartTag(DT_b); |
1662 | xmlWriter().writeAttribute("outputclass", "error"); |
1663 | xmlWriter().writeCharacters("<Missing DITAXML>"); |
1664 | writeEndTag(); // </b> |
1665 | break; |
1666 | case Atom::UnknownCommand: |
1667 | writeStartTag(DT_b); |
1668 | xmlWriter().writeAttribute("outputclass", "error unknown-command"); |
1669 | writeCharacters(protectEnc(atom->string())); |
1670 | writeEndTag(); // </b> |
1671 | break; |
1672 | case Atom::QmlText: |
1673 | case Atom::EndQmlText: |
1674 | // don't do anything with these. They are just tags. |
1675 | break; |
1676 | default: |
1677 | // unknownAtom(atom); |
1678 | break; |
1679 | } |
1680 | return skipAhead; |
1681 | } |
1682 | |
1683 | /*! |
1684 | Generate a <cxxClass> element (and all the stuff inside it) |
1685 | for the C++ class represented by \a innerNode. \a marker is |
1686 | for marking up the code. I don't know what that means exactly. |
1687 | */ |
1688 | void |
1689 | DitaXmlGenerator::generateClassLikeNode(const InnerNode* inner, CodeMarker* marker) |
1690 | { |
1691 | QList<Section>::ConstIterator s; |
1692 | |
1693 | QString title; |
1694 | QString rawTitle; |
1695 | QString fullTitle; |
1696 | if (inner->type() == Node::Namespace) { |
1697 | const NamespaceNode* nsn = const_cast<NamespaceNode*>(static_cast<const NamespaceNode*>(inner)); |
1698 | rawTitle = marker->plainName(inner); |
1699 | fullTitle = marker->plainFullName(inner); |
1700 | title = rawTitle + " Namespace"; |
1701 | |
1702 | /* |
1703 | Note: Because the C++ specialization we are using |
1704 | has no <cxxNamespace> element, we are using the |
1705 | <cxxClass> element with an outputclass attribute |
1706 | set to "namespace" . |
1707 | */ |
1708 | generateHeader(inner, fullTitle); |
1709 | generateBrief(inner, marker); // <shortdesc> |
1710 | writeProlog(inner); |
1711 | |
1712 | writeStartTag(DT_cxxClassDetail); |
1713 | writeStartTag(DT_cxxClassDefinition); |
1714 | writeLocation(nsn); |
1715 | writeEndTag(); // <cxxClassDefinition> |
1716 | |
1717 | enterApiDesc(QString(),title); |
1718 | Text brief = nsn->doc().briefText(); // zzz |
1719 | if (!brief.isEmpty()) { |
1720 | writeStartTag(DT_p); |
1721 | generateText(brief, nsn, marker); |
1722 | writeEndTag(); // </p> |
1723 | } |
1724 | generateIncludes(nsn, marker); |
1725 | generateStatus(nsn, marker); |
1726 | generateThreadSafeness(nsn, marker); |
1727 | generateSince(nsn, marker); |
1728 | |
1729 | enterSection("h2", "Detailed Description"); |
1730 | generateBody(nsn, marker); |
1731 | generateAlsoList(nsn, marker); |
1732 | leaveSection(); |
1733 | leaveSection(); // </apiDesc> |
1734 | |
1735 | bool needOtherSection = false; |
1736 | QList<Section> summarySections; |
1737 | summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay); |
1738 | if (!summarySections.isEmpty()) { |
1739 | enterSection("redundant",QString()); |
1740 | s = summarySections.begin(); |
1741 | while (s != summarySections.end()) { |
1742 | if (s->members.isEmpty() && s->reimpMembers.isEmpty()) { |
1743 | if (!s->inherited.isEmpty()) |
1744 | needOtherSection = true; |
1745 | } |
1746 | else { |
1747 | QString attr; |
1748 | if (!s->members.isEmpty()) { |
1749 | writeStartTag(DT_p); |
1750 | attr = cleanRef((*s).name).toLower() + " h2"; |
1751 | xmlWriter().writeAttribute("outputclass",attr); |
1752 | writeCharacters(protectEnc((*s).name)); |
1753 | writeEndTag(); // </title> |
1754 | generateSection(s->members, inner, marker, CodeMarker::Summary); |
1755 | generateSectionInheritedList(*s, inner, marker); |
1756 | } |
1757 | if (!s->reimpMembers.isEmpty()) { |
1758 | QString name = QString("Reimplemented ") + (*s).name; |
1759 | attr = cleanRef(name).toLower() + " h2"; |
1760 | writeStartTag(DT_p); |
1761 | xmlWriter().writeAttribute("outputclass",attr); |
1762 | writeCharacters(protectEnc(name)); |
1763 | writeEndTag(); // </title> |
1764 | generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary); |
1765 | generateSectionInheritedList(*s, inner, marker); |
1766 | } |
1767 | } |
1768 | ++s; |
1769 | } |
1770 | if (needOtherSection) { |
1771 | writeStartTag(DT_p); |
1772 | xmlWriter().writeAttribute("outputclass", "h3"); |
1773 | xmlWriter().writeCharacters("Additional Inherited Members"); |
1774 | writeEndTag(); // </title> |
1775 | s = summarySections.begin(); |
1776 | while (s != summarySections.end()) { |
1777 | if (s->members.isEmpty()) |
1778 | generateSectionInheritedList(*s, inner, marker); |
1779 | ++s; |
1780 | } |
1781 | } |
1782 | leaveSection(); |
1783 | } |
1784 | |
1785 | writeEndTag(); // </cxxClassDetail> |
1786 | |
1787 | // not included: <related-links> |
1788 | // not included: <cxxClassNested> |
1789 | |
1790 | QList<Section> detailSections; |
1791 | detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay); |
1792 | s = detailSections.begin(); |
1793 | while (s != detailSections.end()) { |
1794 | if ((*s).name == "Classes") { |
1795 | writeNestedClasses((*s),nsn); |
1796 | break; |
1797 | } |
1798 | ++s; |
1799 | } |
1800 | |
1801 | s = detailSections.begin(); |
1802 | while (s != detailSections.end()) { |
1803 | if ((*s).name == "Function Documentation") { |
1804 | writeFunctions((*s),nsn,marker); |
1805 | } |
1806 | else if ((*s).name == "Type Documentation") { |
1807 | writeEnumerations((*s),marker); |
1808 | writeTypedefs((*s),marker); |
1809 | } |
1810 | else if ((*s).name == "Namespaces") { |
1811 | qDebug() << "Nested namespaces"<< outFileName(); |
1812 | } |
1813 | else if ((*s).name == "Macro Documentation") { |
1814 | writeMacros((*s),marker); |
1815 | } |
1816 | ++s; |
1817 | } |
1818 | |
1819 | generateLowStatusMembers(inner,marker,CodeMarker::Obsolete); |
1820 | generateLowStatusMembers(inner,marker,CodeMarker::Compat); |
1821 | writeEndTag(); // </cxxClass> |
1822 | } |
1823 | else if (inner->type() == Node::Class) { |
1824 | const ClassNode* cn = const_cast<ClassNode*>(static_cast<const ClassNode*>(inner)); |
1825 | rawTitle = marker->plainName(inner); |
1826 | fullTitle = marker->plainFullName(inner); |
1827 | title = rawTitle + " Class Reference"; |
1828 | |
1829 | generateHeader(inner, fullTitle); |
1830 | generateBrief(inner, marker); // <shortdesc> |
1831 | writeProlog(inner); |
1832 | |
1833 | writeStartTag(DT_cxxClassDetail); |
1834 | writeStartTag(DT_cxxClassDefinition); |
1835 | writeStartTag(DT_cxxClassAccessSpecifier); |
1836 | xmlWriter().writeAttribute("value",inner->accessString()); |
1837 | writeEndTag(); // <cxxClassAccessSpecifier> |
1838 | if (cn->isAbstract()) { |
1839 | writeStartTag(DT_cxxClassAbstract); |
1840 | xmlWriter().writeAttribute("name", "abstract"); |
1841 | xmlWriter().writeAttribute("value", "abstract"); |
1842 | writeEndTag(); // </cxxClassAbstract> |
1843 | } |
1844 | writeDerivations(cn, marker); // <cxxClassDerivations> |
1845 | |
1846 | // not included: <cxxClassTemplateParameters> |
1847 | |
1848 | writeLocation(cn); |
1849 | writeEndTag(); // <cxxClassDefinition> |
1850 | |
1851 | enterApiDesc(QString(),title); |
1852 | Text brief = cn->doc().briefText(); // zzz |
1853 | if (!brief.isEmpty()) { |
1854 | writeStartTag(DT_p); |
1855 | generateText(brief, cn, marker); |
1856 | writeEndTag(); // </p> |
1857 | } |
1858 | generateIncludes(cn, marker); |
1859 | generateStatus(cn, marker); |
1860 | generateInherits(cn, marker); |
1861 | generateInheritedBy(cn, marker); |
1862 | generateThreadSafeness(cn, marker); |
1863 | generateSince(cn, marker); |
1864 | enterSection("h2", "Detailed Description"); |
1865 | generateBody(cn, marker); |
1866 | generateAlsoList(cn, marker); |
1867 | leaveSection(); |
1868 | leaveSection(); // </apiDesc> |
1869 | |
1870 | bool needOtherSection = false; |
1871 | QList<Section> summarySections; |
1872 | summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay); |
1873 | if (!summarySections.isEmpty()) { |
1874 | enterSection("redundant",QString()); |
1875 | s = summarySections.begin(); |
1876 | while (s != summarySections.end()) { |
1877 | if (s->members.isEmpty() && s->reimpMembers.isEmpty()) { |
1878 | if (!s->inherited.isEmpty()) |
1879 | needOtherSection = true; |
1880 | } |
1881 | else { |
1882 | QString attr; |
1883 | if (!s->members.isEmpty()) { |
1884 | writeStartTag(DT_p); |
1885 | attr = cleanRef((*s).name).toLower() + " h2"; |
1886 | xmlWriter().writeAttribute("outputclass",attr); |
1887 | writeCharacters(protectEnc((*s).name)); |
1888 | writeEndTag(); // </p> |
1889 | generateSection(s->members, inner, marker, CodeMarker::Summary); |
1890 | generateSectionInheritedList(*s, inner, marker); |
1891 | } |
1892 | if (!s->reimpMembers.isEmpty()) { |
1893 | QString name = QString("Reimplemented ") + (*s).name; |
1894 | attr = cleanRef(name).toLower() + " h2"; |
1895 | writeStartTag(DT_p); |
1896 | xmlWriter().writeAttribute("outputclass",attr); |
1897 | writeCharacters(protectEnc(name)); |
1898 | writeEndTag(); // </p> |
1899 | generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary); |
1900 | generateSectionInheritedList(*s, inner, marker); |
1901 | } |
1902 | } |
1903 | ++s; |
1904 | } |
1905 | if (needOtherSection) { |
1906 | writeStartTag(DT_p); |
1907 | xmlWriter().writeAttribute("outputclass", "h3"); |
1908 | xmlWriter().writeCharacters("Additional Inherited Members"); |
1909 | writeEndTag(); // </p> |
1910 | s = summarySections.begin(); |
1911 | while (s != summarySections.end()) { |
1912 | if (s->members.isEmpty()) |
1913 | generateSectionInheritedList(*s, inner, marker); |
1914 | ++s; |
1915 | } |
1916 | } |
1917 | leaveSection(); |
1918 | } |
1919 | |
1920 | // not included: <example> or <apiImpl> |
1921 | |
1922 | writeEndTag(); // </cxxClassDetail> |
1923 | |
1924 | // not included: <related-links> |
1925 | // not included: <cxxClassNested> |
1926 | |
1927 | QList<Section> detailSections; |
1928 | detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay); |
1929 | s = detailSections.begin(); |
1930 | while (s != detailSections.end()) { |
1931 | if ((*s).name == "Member Function Documentation") { |
1932 | writeFunctions((*s),cn,marker); |
1933 | } |
1934 | else if ((*s).name == "Member Type Documentation") { |
1935 | writeEnumerations((*s),marker); |
1936 | writeTypedefs((*s),marker); |
1937 | } |
1938 | else if ((*s).name == "Member Variable Documentation") { |
1939 | writeDataMembers((*s),marker); |
1940 | } |
1941 | else if ((*s).name == "Property Documentation") { |
1942 | writeProperties((*s),marker); |
1943 | } |
1944 | else if ((*s).name == "Macro Documentation") { |
1945 | writeMacros((*s),marker); |
1946 | } |
1947 | ++s; |
1948 | } |
1949 | |
1950 | generateLowStatusMembers(inner,marker,CodeMarker::Obsolete); |
1951 | generateLowStatusMembers(inner,marker,CodeMarker::Compat); |
1952 | writeEndTag(); // </cxxClass> |
1953 | } |
1954 | else if ((inner->type() == Node::Fake) && (inner->subType() == Node::HeaderFile)) { |
1955 | const FakeNode* fn = const_cast<FakeNode*>(static_cast<const FakeNode*>(inner)); |
1956 | rawTitle = marker->plainName(inner); |
1957 | fullTitle = marker->plainFullName(inner); |
1958 | title = rawTitle; |
1959 | |
1960 | /* |
1961 | Note: Because the C++ specialization we are using |
1962 | has no <cxxHeaderFile> element, we are using the |
1963 | <cxxClass> element with an outputclass attribute |
1964 | set to "headerfile" . |
1965 | */ |
1966 | generateHeader(inner, fullTitle); |
1967 | generateBrief(inner, marker); // <shortdesc> |
1968 | writeProlog(inner); |
1969 | |
1970 | writeStartTag(DT_cxxClassDetail); |
1971 | enterApiDesc(QString(),title); |
1972 | Text brief = fn->doc().briefText(); // zzz |
1973 | if (!brief.isEmpty()) { |
1974 | writeStartTag(DT_p); |
1975 | generateText(brief, fn, marker); |
1976 | writeEndTag(); // </p> |
1977 | } |
1978 | generateIncludes(fn, marker); |
1979 | generateStatus(fn, marker); |
1980 | generateThreadSafeness(fn, marker); |
1981 | generateSince(fn, marker); |
1982 | generateSince(fn, marker); |
1983 | enterSection("h2", "Detailed Description"); |
1984 | generateBody(fn, marker); |
1985 | generateAlsoList(fn, marker); |
1986 | leaveSection(); |
1987 | leaveSection(); // </apiDesc> |
1988 | |
1989 | bool needOtherSection = false; |
1990 | QList<Section> summarySections; |
1991 | summarySections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay); |
1992 | if (!summarySections.isEmpty()) { |
1993 | enterSection("redundant",QString()); |
1994 | s = summarySections.begin(); |
1995 | while (s != summarySections.end()) { |
1996 | if (s->members.isEmpty() && s->reimpMembers.isEmpty()) { |
1997 | if (!s->inherited.isEmpty()) |
1998 | needOtherSection = true; |
1999 | } |
2000 | else { |
2001 | QString attr; |
2002 | if (!s->members.isEmpty()) { |
2003 | writeStartTag(DT_p); |
2004 | attr = cleanRef((*s).name).toLower() + " h2"; |
2005 | xmlWriter().writeAttribute("outputclass",attr); |
2006 | writeCharacters(protectEnc((*s).name)); |
2007 | writeEndTag(); // </p> |
2008 | generateSection(s->members, inner, marker, CodeMarker::Summary); |
2009 | generateSectionInheritedList(*s, inner, marker); |
2010 | } |
2011 | if (!s->reimpMembers.isEmpty()) { |
2012 | QString name = QString("Reimplemented ") + (*s).name; |
2013 | attr = cleanRef(name).toLower() + " h2"; |
2014 | writeStartTag(DT_p); |
2015 | xmlWriter().writeAttribute("outputclass",attr); |
2016 | writeCharacters(protectEnc(name)); |
2017 | writeEndTag(); // </p> |
2018 | generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary); |
2019 | generateSectionInheritedList(*s, inner, marker); |
2020 | } |
2021 | } |
2022 | ++s; |
2023 | } |
2024 | if (needOtherSection) { |
2025 | enterSection("additional-inherited-members redundant",QString()); |
2026 | writeStartTag(DT_p); |
2027 | xmlWriter().writeAttribute("outputclass", "h3"); |
2028 | xmlWriter().writeCharacters("Additional Inherited Members"); |
2029 | writeEndTag(); // </p> |
2030 | s = summarySections.begin(); |
2031 | while (s != summarySections.end()) { |
2032 | if (s->members.isEmpty()) |
2033 | generateSectionInheritedList(*s, inner, marker); |
2034 | ++s; |
2035 | } |
2036 | } |
2037 | leaveSection(); |
2038 | } |
2039 | |
2040 | writeEndTag(); // </cxxClassDetail> |
2041 | |
2042 | // not included: <related-links> |
2043 | // not included: <cxxClassNested> |
2044 | |
2045 | QList<Section> detailSections; |
2046 | detailSections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay); |
2047 | s = detailSections.begin(); |
2048 | while (s != detailSections.end()) { |
2049 | if ((*s).name == "Classes") { |
2050 | writeNestedClasses((*s),fn); |
2051 | break; |
2052 | } |
2053 | ++s; |
2054 | } |
2055 | |
2056 | s = detailSections.begin(); |
2057 | while (s != detailSections.end()) { |
2058 | if ((*s).name == "Function Documentation") { |
2059 | writeFunctions((*s),fn,marker); |
2060 | } |
2061 | else if ((*s).name == "Type Documentation") { |
2062 | writeEnumerations((*s),marker); |
2063 | writeTypedefs((*s),marker); |
2064 | } |
2065 | else if ((*s).name == "Namespaces") { |
2066 | qDebug() << "Nested namespaces"<< outFileName(); |
2067 | } |
2068 | else if ((*s).name == "Macro Documentation") { |
2069 | writeMacros((*s),marker); |
2070 | } |
2071 | ++s; |
2072 | } |
2073 | generateLowStatusMembers(inner,marker,CodeMarker::Obsolete); |
2074 | generateLowStatusMembers(inner,marker,CodeMarker::Compat); |
2075 | writeEndTag(); // </cxxClass> |
2076 | } |
2077 | else if ((inner->type() == Node::Fake) && (inner->subType() == Node::QmlClass)) { |
2078 | const QmlClassNode* qcn = const_cast<QmlClassNode*>(static_cast<const QmlClassNode*>(inner)); |
2079 | const ClassNode* cn = qcn->classNode(); |
2080 | rawTitle = marker->plainName(inner); |
2081 | fullTitle = marker->plainFullName(inner); |
2082 | title = rawTitle + " Element Reference"; |
2083 | //QString fullTitle = fake->fullTitle(); |
2084 | //QString htmlTitle = fullTitle; |
2085 | |
2086 | generateHeader(inner, fullTitle); |
2087 | generateBrief(inner, marker); // <shortdesc> |
2088 | writeProlog(inner); |
2089 | |
2090 | writeStartTag(DT_cxxClassDetail); |
2091 | enterApiDesc(QString(),title); |
2092 | Text brief = qcn->doc().briefText(); // zzz |
2093 | if (!brief.isEmpty()) { |
2094 | writeStartTag(DT_p); |
2095 | generateText(brief, qcn, marker); |
2096 | writeEndTag(); // </p> |
2097 | } |
2098 | generateQmlInstantiates(qcn, marker); |
2099 | generateQmlInherits(qcn, marker); |
2100 | generateQmlInheritedBy(qcn, marker); |
2101 | generateSince(qcn, marker); |
2102 | enterSection("h2", "Detailed Description"); |
2103 | generateBody(qcn, marker); |
2104 | if (cn) { |
2105 | generateQmlText(cn->doc().body(), cn, marker, qcn->name()); |
2106 | generateAlsoList(cn, marker); |
2107 | } |
2108 | leaveSection(); |
2109 | leaveSection(); // </apiDesc> |
2110 | |
2111 | QList<Section> summarySections; |
2112 | summarySections = marker->qmlSections(qcn,CodeMarker::Summary,0); |
2113 | if (!summarySections.isEmpty()) { |
2114 | enterSection("redundant",QString()); |
2115 | s = summarySections.begin(); |
2116 | while (s != summarySections.end()) { |
2117 | QString attr; |
2118 | if (!s->members.isEmpty()) { |
2119 | writeStartTag(DT_p); |
2120 | attr = cleanRef((*s).name).toLower() + " h2"; |
2121 | xmlWriter().writeAttribute("outputclass",attr); |
2122 | writeCharacters(protectEnc((*s).name)); |
2123 | writeEndTag(); // </p> |
2124 | generateQmlSummary(*s,qcn,marker); |
2125 | //generateSection(s->members, inner, marker, CodeMarker::Summary); |
2126 | //generateSectionInheritedList(*s, inner, marker); |
2127 | } |
2128 | ++s; |
2129 | } |
2130 | leaveSection(); |
2131 | } |
2132 | |
2133 | QList<Section> detailSections; |
2134 | detailSections = marker->qmlSections(qcn,CodeMarker::Detailed,0); |
2135 | if (!detailSections.isEmpty()) { |
2136 | enterSection("details",QString()); |
2137 | s = detailSections.begin(); |
2138 | while (s != detailSections.end()) { |
2139 | if (!s->members.isEmpty()) { |
2140 | QString attr; |
2141 | writeStartTag(DT_p); |
2142 | attr = cleanRef((*s).name).toLower() + " h2"; |
2143 | xmlWriter().writeAttribute("outputclass",attr); |
2144 | writeCharacters(protectEnc((*s).name)); |
2145 | writeEndTag(); // </p> |
2146 | NodeList::ConstIterator m = (*s).members.begin(); |
2147 | while (m != (*s).members.end()) { |
2148 | generateDetailedQmlMember(*m, qcn, marker); |
2149 | ++m; |
2150 | } |
2151 | } |
2152 | ++s; |
2153 | } |
2154 | leaveSection(); |
2155 | } |
2156 | writeEndTag(); // </cxxClassDetail> |
2157 | writeEndTag(); // </cxxClass> |
2158 | } |
2159 | } |
2160 | |
2161 | |
2162 | /*! |
2163 | Write a list item for a \a link with the given \a text. |
2164 | */ |
2165 | void DitaXmlGenerator::writeXrefListItem(const QString& link, const QString& text) |
2166 | { |
2167 | writeStartTag(DT_li); |
2168 | writeStartTag(DT_xref); |
2169 | // formathtml |
2170 | xmlWriter().writeAttribute("href",link); |
2171 | writeCharacters(text); |
2172 | writeEndTag(); // </xref> |
2173 | writeEndTag(); // </li> |
2174 | } |
2175 | |
2176 | /*! |
2177 | Generate the html page for a qdoc file that doesn't map |
2178 | to an underlying c++ file. |
2179 | */ |
2180 | void DitaXmlGenerator::generateFakeNode(const FakeNode* fake, CodeMarker* marker) |
2181 | { |
2182 | QList<Section> sections; |
2183 | QList<Section>::const_iterator s; |
2184 | QString fullTitle = fake->fullTitle(); |
2185 | |
2186 | if (fake->subType() == Node::QmlBasicType) { |
2187 | fullTitle = "QML Basic Type: "+ fullTitle; |
2188 | } |
2189 | |
2190 | generateHeader(fake, fullTitle); |
2191 | generateBrief(fake, marker); // <shortdesc> |
2192 | writeProlog(fake); |
2193 | |
2194 | writeStartTag(DT_body); |
2195 | enterSection(QString(),QString()); |
2196 | if (fake->subType() == Node::Module) { |
2197 | generateStatus(fake, marker); |
2198 | if (moduleNamespaceMap.contains(fake->name())) { |
2199 | enterSection("h2", "Namespaces"); |
2200 | generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]); |
2201 | leaveSection(); |
2202 | } |
2203 | if (moduleClassMap.contains(fake->name())) { |
2204 | enterSection("h2", "Classes"); |
2205 | generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]); |
2206 | leaveSection(); |
2207 | } |
2208 | } |
2209 | |
2210 | if (fake->doc().isEmpty()) { |
2211 | if (fake->subType() == Node::File) { |
2212 | Text text; |
2213 | Quoter quoter; |
2214 | writeStartTag(DT_p); |
2215 | xmlWriter().writeAttribute("outputclass", "small-subtitle"); |
2216 | text << fake->subTitle(); |
2217 | generateText(text, fake, marker); |
2218 | writeEndTag(); // </p> |
2219 | Doc::quoteFromFile(fake->doc().location(), quoter, fake->name()); |
2220 | QString code = quoter.quoteTo(fake->location(), "", ""); |
2221 | text.clear(); |
2222 | text << Atom(Atom::Code, code); |
2223 | generateText(text, fake, marker); |
2224 | } |
2225 | } |
2226 | else { |
2227 | if (fake->subType() == Node::Module) { |
2228 | enterSection("h2", "Detailed Description"); |
2229 | generateBody(fake, marker); |
2230 | leaveSection(); |
2231 | } |
2232 | else |
2233 | generateBody(fake, marker); |
2234 | generateAlsoList(fake, marker); |
2235 | |
2236 | if (!fake->groupMembers().isEmpty()) { |
2237 | NodeMap groupMembersMap; |
2238 | foreach (const Node *node, fake->groupMembers()) { |
2239 | if (node->type() == Node::Class || node->type() == Node::Namespace) |
2240 | groupMembersMap[node->name()] = node; |
2241 | } |
2242 | generateAnnotatedList(fake, marker, groupMembersMap); |
2243 | } |
2244 | } |
2245 | leaveSection(); // </section> |
2246 | writeEndTag(); // </body> |
2247 | writeRelatedLinks(fake, marker); |
2248 | writeEndTag(); // </topic> |
2249 | } |
2250 | |
2251 | /*! |
2252 | This function writes a \e{<link>} element inside a |
2253 | \e{<related-links>} element. |
2254 | |
2255 | \sa writeRelatedLinks() |
2256 | */ |
2257 | void DitaXmlGenerator::writeLink(const Node* node, |
2258 | const QString& text, |
2259 | const QString& role) |
2260 | { |
2261 | if (node) { |
2262 | QString link = fileName(node) + "#"+ node->guid(); |
2263 | writeStartTag(DT_link); |
2264 | xmlWriter().writeAttribute("href", link); |
2265 | xmlWriter().writeAttribute("role", role); |
2266 | writeStartTag(DT_linktext); |
2267 | writeCharacters(text); |
2268 | writeEndTag(); // </linktext> |
2269 | writeEndTag(); // </link> |
2270 | } |
2271 | } |
2272 | |
2273 | /*! |
2274 | This function writes a \e{<related-links>} element, which |
2275 | contains the \c{next}, \c{previous}, and \c{start} |
2276 | links for topic pages that have them. Note that the |
2277 | value of the \e role attribute is \c{parent} for the |
2278 | \c{start} link. |
2279 | */ |
2280 | void DitaXmlGenerator::writeRelatedLinks(const FakeNode* node, CodeMarker* marker) |
2281 | { |
2282 | const Node* linkNode = 0; |
2283 | QPair<QString,QString> linkPair; |
2284 | if (node && !node->links().empty()) { |
2285 | writeStartTag(DT_relatedLinks); |
2286 | if (node->links().contains(Node::PreviousLink)) { |
2287 | linkPair = node->links()[Node::PreviousLink]; |
2288 | linkNode = findNodeForTarget(linkPair.first, node, marker); |
2289 | writeLink(linkNode, linkPair.second, "previous"); |
2290 | } |
2291 | if (node->links().contains(Node::NextLink)) { |
2292 | linkPair = node->links()[Node::NextLink]; |
2293 | linkNode = findNodeForTarget(linkPair.first, node, marker); |
2294 | writeLink(linkNode, linkPair.second, "next"); |
2295 | } |
2296 | if (node->links().contains(Node::StartLink)) { |
2297 | linkPair = node->links()[Node::StartLink]; |
2298 | linkNode = findNodeForTarget(linkPair.first, node, marker); |
2299 | writeLink(linkNode, linkPair.second, "parent"); |
2300 | } |
2301 | writeEndTag(); // </related-links> |
2302 | } |
2303 | } |
2304 | |
2305 | /*! |
2306 | Returns "xml" for this subclass of class Generator. |
2307 | */ |
2308 | QString DitaXmlGenerator::fileExtension(const Node * /* node */) const |
2309 | { |
2310 | return "xml"; |
2311 | } |
2312 | |
2313 | /*! |
2314 | Writes an XML file header to the current XML stream. This |
2315 | depends on which kind of DITA XML file is being generated, |
2316 | which is determined by the \a node type and subtype and the |
2317 | \a subpage flag. If the \subpage flag is true, a \c{<topic>} |
2318 | header is written, regardless of the type of \a node. |
2319 | */ |
2320 | void DitaXmlGenerator::generateHeader(const Node* node, |
2321 | const QString& name, |
2322 | bool subpage) |
2323 | { |
2324 | if (!node) |
2325 | return; |
2326 | |
2327 | DitaTag mainTag = DT_cxxClass; |
2328 | DitaTag nameTag = DT_apiName; |
2329 | QString doctype; |
2330 | QString dtd; |
2331 | QString base; |
2332 | QString version; |
2333 | QString outputclass; |
2334 | |
2335 | if (node->type() == Node::Class) { |
2336 | mainTag = DT_cxxClass; |
2337 | nameTag = DT_apiName; |
2338 | dtd = "dtd/cxxClass.dtd"; |
2339 | version = "0.6.0"; |
2340 | doctype = "<!DOCTYPE "+ ditaTags[mainTag] + |
2341 | " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v"+ |
2342 | version + "//EN\" \""+ dtd + "\">"; |
2343 | } |
2344 | else if (node->type() == Node::Namespace) { |
2345 | mainTag = DT_cxxClass; |
2346 | nameTag = DT_apiName; |
2347 | dtd = "dtd/cxxClass.dtd"; |
2348 | version = "0.6.0"; |
2349 | doctype = "<!DOCTYPE "+ ditaTags[mainTag] + |
2350 | " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v"+ |
2351 | version + "//EN\" \""+ dtd + "\">"; |
2352 | outputclass = "namespace"; |
2353 | } |
2354 | else if (node->type() == Node::Fake || subpage) { |
2355 | if (node->subType() == Node::HeaderFile) { |
2356 | mainTag = DT_cxxClass; |
2357 | nameTag = DT_apiName; |
2358 | dtd = "dtd/cxxClass.dtd"; |
2359 | version = "0.6.0"; |
2360 | doctype = "<!DOCTYPE "+ ditaTags[mainTag] + |
2361 | " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v"+ |
2362 | version + "//EN\" \""+ dtd + "\">"; |
2363 | outputclass = "headerfile"; |
2364 | } |
2365 | else if (node->subType() == Node::QmlClass) { |
2366 | mainTag = DT_cxxClass; |
2367 | nameTag = DT_apiName; |
2368 | dtd = "dtd/cxxClass.dtd"; |
2369 | version = "0.6.0"; |
2370 | doctype = "<!DOCTYPE "+ ditaTags[mainTag] + |
2371 | " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v"+ |
2372 | version + "//EN\" \""+ dtd + "\">"; |
2373 | outputclass = "QML-class"; |
2374 | } |
2375 | else { |
2376 | mainTag = DT_topic; |
2377 | nameTag = DT_title; |
2378 | dtd = "dtd/topic.dtd"; |
2379 | doctype = "<!DOCTYPE "+ ditaTags[mainTag] + |
2380 | " PUBLIC \"-//OASIS//DTD DITA Topic//EN\" \""+ dtd + "\">"; |
2381 | switch (node->subType()) { |
2382 | case Node::Page: |
2383 | outputclass = "page"; |
2384 | break; |
2385 | case Node::Group: |
2386 | outputclass = "group"; |
2387 | break; |
2388 | case Node::Example: |
2389 | outputclass = "example"; |
2390 | break; |
2391 | case Node::File: |
2392 | outputclass = "file"; |
2393 | break; |
2394 | case Node::Image: // not used |
2395 | outputclass = "image"; |
2396 | break; |
2397 | case Node::Module: |
2398 | outputclass = "module"; |
2399 | break; |
2400 | case Node::ExternalPage: // not used |
2401 | outputclass = "externalpage"; |
2402 | break; |
2403 | default: |
2404 | outputclass = "page"; |
2405 | } |
2406 | } |
2407 | } |
2408 | |
2409 | xmlWriter().writeDTD(doctype); |
2410 | xmlWriter().writeComment(node->doc().location().fileName()); |
2411 | writeStartTag(mainTag); |
2412 | xmlWriter().writeAttribute("id",node->guid()); |
2413 | if (!outputclass.isEmpty()) |
2414 | xmlWriter().writeAttribute("outputclass",outputclass); |
2415 | writeStartTag(nameTag); // <title> or <apiName> |
2416 | writeCharacters(name); |
2417 | writeEndTag(); // </title> or </apiName> |
2418 | } |
2419 | |
2420 | /*! |
2421 | Outputs the \e brief command as a <shortdesc> element. |
2422 | */ |
2423 | void DitaXmlGenerator::generateBrief(const Node* node, CodeMarker* marker) |
2424 | { |
2425 | Text brief = node->doc().briefText(true); // zzz |
2426 | if (!brief.isEmpty()) { |
2427 | generateText(brief, node, marker); |
2428 | } |
2429 | } |
2430 | |
2431 | /*! |
2432 | Writes the \c {#include ...} required to include the class |
2433 | or namespace in a compilation. |
2434 | */ |
2435 | void DitaXmlGenerator::generateIncludes(const InnerNode* inner, CodeMarker* marker) |
2436 | { |
2437 | if (!inner->includes().isEmpty()) { |
2438 | writeStartTag(DT_codeblock); |
2439 | writeText(marker->markedUpIncludes(inner->includes()), marker, inner); |
2440 | writeEndTag(); // </codeblock> |
2441 | } |
2442 | } |
2443 | |
2444 | /*! |
2445 | zzz |
2446 | Generates a table of contents beginning at \a node. |
2447 | Currently just returns without writing anything. |
2448 | */ |
2449 | void DitaXmlGenerator::generateTableOfContents(const Node* node, |
2450 | CodeMarker* marker, |
2451 | Doc::Sections sectionUnit, |
2452 | int numColumns, |
2453 | const Node* relative) |
2454 | |
2455 | { |
2456 | return; |
2457 | if (!node->doc().hasTableOfContents()) |
2458 | return; |
2459 | QList<Atom *> toc = node->doc().tableOfContents(); |
2460 | if (toc.isEmpty()) |
2461 | return; |
2462 | |
2463 | QString nodeName = ""; |
2464 | if (node != relative) |
2465 | nodeName = node->name(); |
2466 | |
2467 | QStringList sectionNumber; |
2468 | int columnSize = 0; |
2469 | |
2470 | QString tdTag; |
2471 | if (numColumns > 1) { |
2472 | tdTag = "<td>"; /* width=\"" + QString::number((100 + numColumns - 1) / numColumns) + "%\">";*/ |
2473 | out() << "<table class=\"toc\">\n<tr class=\"topAlign\">" |
2474 | << tdTag << "\n"; |
2475 | } |
2476 | |
2477 | // disable nested links in table of contents |
2478 | inContents = true; |
2479 | inLink = true; |
2480 | |
2481 | for (int i = 0; i < toc.size(); ++i) { |
2482 | Atom *atom = toc.at(i); |
2483 | |
2484 | int nextLevel = atom->string().toInt(); |
2485 | if (nextLevel > (int)sectionUnit) |
2486 | continue; |
2487 | |
2488 | if (sectionNumber.size() < nextLevel) { |
2489 | do { |
2490 | out() << "<ul>"; |
2491 | sectionNumber.append("1"); |
2492 | } while (sectionNumber.size() < nextLevel); |
2493 | } |
2494 | else { |
2495 | while (sectionNumber.size() > nextLevel) { |
2496 | out() << "</ul>\n"; |
2497 | sectionNumber.removeLast(); |
2498 | } |
2499 | sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1); |
2500 | } |
2501 | int numAtoms; |
2502 | Text headingText = Text::sectionHeading(atom); |
2503 | |
2504 | if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) { |
2505 | out() << "</ul></td>"<< tdTag << "<ul>\n"; |
2506 | columnSize = 0; |
2507 | } |
2508 | out() << "<li>"; |
2509 | out() << "<xref href=\"" |
2510 | << nodeName |
2511 | << "#" |
2512 | << Doc::canonicalTitle(headingText.toString()) |
2513 | << "\">"; |
2514 | generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms); |
2515 | out() << "</xref></li>\n"; |
2516 | |
2517 | ++columnSize; |
2518 | } |
2519 | while (!sectionNumber.isEmpty()) { |
2520 | out() << "</ul>\n"; |
2521 | sectionNumber.removeLast(); |
2522 | } |
2523 | |
2524 | if (numColumns > 1) |
2525 | out() << "</td></tr></table>\n"; |
2526 | |
2527 | inContents = false; |
2528 | inLink = false; |
2529 | } |
2530 | |
2531 | /*! |
2532 | zzz |
2533 | Revised for the new doc format. |
2534 | Generates a table of contents beginning at \a node. |
2535 | */ |
2536 | void DitaXmlGenerator::generateTableOfContents(const Node* node, |
2537 | CodeMarker* marker, |
2538 | QList<Section>* sections) |
2539 | { |
2540 | QList<Atom*> toc; |
2541 | if (node->doc().hasTableOfContents()) |
2542 | toc = node->doc().tableOfContents(); |
2543 | if (toc.isEmpty() && !sections && (node->subType() != Node::Module)) |
2544 | return; |
2545 | |
2546 | QStringList sectionNumber; |
2547 | int detailsBase = 0; |
2548 | |
2549 | // disable nested links in table of contents |
2550 | inContents = true; |
2551 | inLink = true; |
2552 | |
2553 | out() << "<div class=\"toc\">\n"; |
2554 | out() << "<h3>Contents</h3>\n"; |
2555 | sectionNumber.append("1"); |
2556 | out() << "<ul>\n"; |
2557 | |
2558 | if (node->subType() == Node::Module) { |
2559 | if (moduleNamespaceMap.contains(node->name())) { |
2560 | out() << "<li class=\"level" |
2561 | << sectionNumber.size() |
2562 | << "\"><xref href=\"#" |
2563 | << registerRef("namespaces") |
2564 | << "\">Namespaces</xref></li>\n"; |
2565 | } |
2566 | if (moduleClassMap.contains(node->name())) { |
2567 | out() << "<li class=\"level" |
2568 | << sectionNumber.size() |
2569 | << "\"><xref href=\"#" |
2570 | << registerRef("classes") |
2571 | << "\">Classes</xref></li>\n"; |
2572 | } |
2573 | out() << "<li class=\"level" |
2574 | << sectionNumber.size() |
2575 | << "\"><xref href=\"#" |
2576 | << registerRef("details") |
2577 | << "\">Detailed Description</xref></li>\n"; |
2578 | for (int i = 0; i < toc.size(); ++i) { |
2579 | if (toc.at(i)->string().toInt() == 1) { |
2580 | detailsBase = 1; |
2581 | break; |
2582 | } |
2583 | } |
2584 | } |
2585 | else if (sections && (node->type() == Node::Class)) { |
2586 | QList<Section>::ConstIterator s = sections->begin(); |
2587 | while (s != sections->end()) { |
2588 | if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) { |
2589 | out() << "<li class=\"level" |
2590 | << sectionNumber.size() |
2591 | << "\"><xref href=\"#" |
2592 | << registerRef((*s).pluralMember) |
2593 | << "\">"<< (*s).name |
2594 | << "</xref></li>\n"; |
2595 | } |
2596 | ++s; |
2597 | } |
2598 | out() << "<li class=\"level" |
2599 | << sectionNumber.size() |
2600 | << "\"><xref href=\"#" |
2601 | << registerRef("details") |
2602 | << "\">Detailed Description</xref></li>\n"; |
2603 | for (int i = 0; i < toc.size(); ++i) { |
2604 | if (toc.at(i)->string().toInt() == 1) { |
2605 | detailsBase = 1; |
2606 | break; |
2607 | } |
2608 | } |
2609 | } |
2610 | |
2611 | for (int i = 0; i < toc.size(); ++i) { |
2612 | Atom *atom = toc.at(i); |
2613 | int nextLevel = atom->string().toInt() + detailsBase; |
2614 | if (sectionNumber.size() < nextLevel) { |
2615 | do { |
2616 | sectionNumber.append("1"); |
2617 | } while (sectionNumber.size() < nextLevel); |
2618 | } |
2619 | else { |
2620 | while (sectionNumber.size() > nextLevel) { |
2621 | sectionNumber.removeLast(); |
2622 | } |
2623 | sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1); |
2624 | } |
2625 | int numAtoms; |
2626 | Text headingText = Text::sectionHeading(atom); |
2627 | QString s = headingText.toString(); |
2628 | out() << "<li class=\"level" |
2629 | << sectionNumber.size() |
2630 | << "\">"; |
2631 | out() << "<xref href=\"" |
2632 | << "#" |
2633 | << Doc::canonicalTitle(s) |
2634 | << "\">"; |
2635 | generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms); |
2636 | out() << "</xref></li>\n"; |
2637 | } |
2638 | while (!sectionNumber.isEmpty()) { |
2639 | sectionNumber.removeLast(); |
2640 | } |
2641 | out() << "</ul>\n"; |
2642 | out() << "</div>\n"; |
2643 | inContents = false; |
2644 | inLink = false; |
2645 | } |
2646 | |
2647 | void DitaXmlGenerator::generateLowStatusMembers(const InnerNode* inner, |
2648 | CodeMarker* marker, |
2649 | CodeMarker::Status status) |
2650 | { |
2651 | QString attribute; |
2652 | if (status == CodeMarker::Compat) |
2653 | attribute = "Qt3-support"; |
2654 | else if (status == CodeMarker::Obsolete) |
2655 | attribute = "obsolete"; |
2656 | else |
2657 | return; |
2658 | |
2659 | QList<Section> sections = marker->sections(inner, CodeMarker::Detailed, status); |
2660 | QMutableListIterator<Section> j(sections); |
2661 | while (j.hasNext()) { |
2662 | if (j.next().members.size() == 0) |
2663 | j.remove(); |
2664 | } |
2665 | if (sections.isEmpty()) |
2666 | return; |
2667 | |
2668 | QList<Section>::ConstIterator s = sections.begin(); |
2669 | while (s != sections.end()) { |
2670 | if ((*s).name == "Member Function Documentation") { |
2671 | writeFunctions((*s),inner,marker,attribute); |
2672 | } |
2673 | else if ((*s).name == "Member Type Documentation") { |
2674 | writeEnumerations((*s),marker,attribute); |
2675 | writeTypedefs((*s),marker,attribute); |
2676 | } |
2677 | else if ((*s).name == "Member Variable Documentation") { |
2678 | writeDataMembers((*s),marker,attribute); |
2679 | } |
2680 | else if ((*s).name == "Property Documentation") { |
2681 | writeProperties((*s),marker,attribute); |
2682 | } |
2683 | else if ((*s).name == "Macro Documentation") { |
2684 | writeMacros((*s),marker,attribute); |
2685 | } |
2686 | ++s; |
2687 | } |
2688 | } |
2689 | |
2690 | /*! |
2691 | Write the XML for the class hierarchy to the current XML stream. |
2692 | */ |
2693 | void DitaXmlGenerator::generateClassHierarchy(const Node* relative, |
2694 | CodeMarker* marker, |
2695 | const QMap<QString,const Node*>& classMap) |
2696 | { |
2697 | if (classMap.isEmpty()) |
2698 | return; |
2699 | |
2700 | NodeMap topLevel; |
2701 | NodeMap::ConstIterator c = classMap.begin(); |
2702 | while (c != classMap.end()) { |
2703 | const ClassNode* classe = static_cast<const ClassNode*>(*c); |
2704 | if (classe->baseClasses().isEmpty()) |
2705 | topLevel.insert(classe->name(), classe); |
2706 | ++c; |
2707 | } |
2708 | |
2709 | QStack<NodeMap > stack; |
2710 | stack.push(topLevel); |
2711 | |
2712 | writeStartTag(DT_ul); |
2713 | while (!stack.isEmpty()) { |
2714 | if (stack.top().isEmpty()) { |
2715 | stack.pop(); |
2716 | writeEndTag(); // </ul> |
2717 | if (!stack.isEmpty()) |
2718 | writeEndTag(); // </li> |
2719 | } |
2720 | else { |
2721 | const ClassNode *child = |
2722 | static_cast<const ClassNode *>(*stack.top().begin()); |
2723 | writeStartTag(DT_li); |
2724 | generateFullName(child, relative, marker); |
2725 | writeEndTag(); // </li> |
2726 | stack.top().erase(stack.top().begin()); |
2727 | |
2728 | NodeMap newTop; |
2729 | foreach (const RelatedClass &d, child->derivedClasses()) { |
2730 | if (d.access != Node::Private && !d.node->doc().isEmpty()) |
2731 | newTop.insert(d.node->name(), d.node); |
2732 | } |
2733 | if (!newTop.isEmpty()) { |
2734 | stack.push(newTop); |
2735 | writeStartTag(DT_li); |
2736 | writeStartTag(DT_ul); |
2737 | } |
2738 | } |
2739 | } |
2740 | } |
2741 | |
2742 | /*! |
2743 | Write XML for the contents of the \a nodeMap to the current |
2744 | XML stream. |
2745 | */ |
2746 | void DitaXmlGenerator::generateAnnotatedList(const Node* relative, |
2747 | CodeMarker* marker, |
2748 | const NodeMap& nodeMap) |
2749 | { |
2750 | if (nodeMap.isEmpty()) |
2751 | return; |
2752 | writeStartTag(DT_table); |
2753 | xmlWriter().writeAttribute("outputclass", "annotated"); |
2754 | writeStartTag(DT_tgroup); |
2755 | xmlWriter().writeAttribute("cols", "2"); |
2756 | writeStartTag(DT_tbody); |
2757 | |
2758 | foreach (const QString& name, nodeMap.keys()) { |
2759 | const Node* node = nodeMap[name]; |
2760 | |
2761 | if (node->status() == Node::Obsolete) |
2762 | continue; |
2763 | |
2764 | writeStartTag(DT_row); |
2765 | writeStartTag(DT_entry); |
2766 | writeStartTag(DT_p); |
2767 | generateFullName(node, relative, marker); |
2768 | writeEndTag(); // </p> |
2769 | writeEndTag(); // <entry> |
2770 | |
2771 | if (!(node->type() == Node::Fake)) { |
2772 | Text brief = node->doc().trimmedBriefText(name); |
2773 | if (!brief.isEmpty()) { |
2774 | writeStartTag(DT_entry); |
2775 | writeStartTag(DT_p); |
2776 | generateText(brief, node, marker); |
2777 | writeEndTag(); // </p> |
2778 | writeEndTag(); // <entry> |
2779 | } |
2780 | } |
2781 | else { |
2782 | writeStartTag(DT_entry); |
2783 | writeStartTag(DT_p); |
2784 | writeCharacters(protectEnc(node->doc().briefText().toString())); // zzz |
2785 | writeEndTag(); // </p> |
2786 | writeEndTag(); // <entry> |
2787 | } |
2788 | writeEndTag(); // </row> |
2789 | } |
2790 | writeEndTag(); // </tbody> |
2791 | writeEndTag(); // </tgroup> |
2792 | writeEndTag(); // </table> |
2793 | } |
2794 | |
2795 | /*! |
2796 | This function finds the common prefix of the names of all |
2797 | the classes in \a classMap and then generates a compact |
2798 | list of the class names alphabetized on the part of the |
2799 | name not including the common prefix. You can tell the |
2800 | function to use \a comonPrefix as the common prefix, but |
2801 | normally you let it figure it out itself by looking at |
2802 | the name of the first and last classes in \a classMap. |
2803 | */ |
2804 | void DitaXmlGenerator::generateCompactList(const Node* relative, |
2805 | CodeMarker* marker, |
2806 | const NodeMap& classMap, |
2807 | bool includeAlphabet, |
2808 | QString commonPrefix) |
2809 | { |
2810 | const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_' |
2811 | |
2812 | if (classMap.isEmpty()) |
2813 | return; |
2814 | |
2815 | /* |
2816 | If commonPrefix is not empty, then the caller knows what |
2817 | the common prefix is and has passed it in, so just use that |
2818 | one. |
2819 | */ |
2820 | int commonPrefixLen = commonPrefix.length(); |
2821 | if (commonPrefixLen == 0) { |
2822 | QString first; |
2823 | QString last; |
2824 | |
2825 | /* |
2826 | The caller didn't pass in a common prefix, so get the common |
2827 | prefix by looking at the class names of the first and last |
2828 | classes in the class map. Discard any namespace names and |
2829 | just use the bare class names. For Qt, the prefix is "Q". |
2830 | |
2831 | Note that the algorithm used here to derive the common prefix |
2832 | from the first and last classes in alphabetical order (QAccel |
2833 | and QXtWidget in Qt 2.1), fails if either class name does not |
2834 | begin with Q. |
2835 | */ |
2836 | |
2837 | NodeMap::const_iterator iter = classMap.begin(); |
2838 | while (iter != classMap.end()) { |
2839 | if (!iter.key().contains("::")) { |
2840 | first = iter.key(); |
2841 | break; |
2842 | } |
2843 | ++iter; |
2844 | } |
2845 | |
2846 | if (first.isEmpty()) |
2847 | first = classMap.begin().key(); |
2848 | |
2849 | iter = classMap.end(); |
2850 | while (iter != classMap.begin()) { |
2851 | --iter; |
2852 | if (!iter.key().contains("::")) { |
2853 | last = iter.key(); |
2854 | break; |
2855 | } |
2856 | } |
2857 | |
2858 | if (last.isEmpty()) |
2859 | last = classMap.begin().key(); |
2860 | |
2861 | if (classMap.size() > 1) { |
2862 | while (commonPrefixLen < first.length() + 1 && |
2863 | commonPrefixLen < last.length() + 1 && |
2864 | first[commonPrefixLen] == last[commonPrefixLen]) |
2865 | ++commonPrefixLen; |
2866 | } |
2867 | |
2868 | commonPrefix = first.left(commonPrefixLen); |
2869 | } |
2870 | |
2871 | /* |
2872 | Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z, |
2873 | underscore (_). QAccel will fall in paragraph 10 (A) and |
2874 | QXtWidget in paragraph 33 (X). This is the only place where we |
2875 | assume that NumParagraphs is 37. Each paragraph is a NodeMap. |
2876 | */ |
2877 | NodeMap paragraph[NumParagraphs+1]; |
2878 | QString paragraphName[NumParagraphs+1]; |
2879 | QSet<char> usedParagraphNames; |
2880 | |
2881 | NodeMap::ConstIterator c = classMap.begin(); |
2882 | while (c != classMap.end()) { |
2883 | QStringList pieces = c.key().split("::"); |
2884 | QString key; |
2885 | int idx = commonPrefixLen; |
2886 | if (!pieces.last().startsWith(commonPrefix)) |
2887 | idx = 0; |
2888 | if (pieces.size() == 1) |
2889 | key = pieces.last().mid(idx).toLower(); |
2890 | else |
2891 | key = pieces.last().toLower(); |
2892 | |
2893 | int paragraphNr = NumParagraphs - 1; |
2894 | |
2895 | if (key[0].digitValue() != -1) { |
2896 | paragraphNr = key[0].digitValue(); |
2897 | } |
2898 | else if (key[0] >= QLatin1Char( |
2899 | paragraphNr = 10 + key[0].unicode() - |
2900 | } |
2901 | |
2902 | paragraphName[paragraphNr] = key[0].toUpper(); |
2903 | usedParagraphNames.insert(key[0].toLower().cell()); |
2904 | paragraph[paragraphNr].insert(key, c.value()); |
2905 | ++c; |
2906 | } |
2907 | |
2908 | /* |
2909 | Each paragraph j has a size: paragraph[j].count(). In the |
2910 | discussion, we will assume paragraphs 0 to 5 will have sizes |
2911 | 3, 1, 4, 1, 5, 9. |
2912 | |
2913 | We now want to compute the paragraph offset. Paragraphs 0 to 6 |
2914 | start at offsets 0, 3, 4, 8, 9, 14, 23. |
2915 | */ |
2916 | int paragraphOffset[NumParagraphs + 1]; // 37 + 1 |
2917 | paragraphOffset[0] = 0; |
2918 | for (int i=0; i<NumParagraphs; i++) // i = 0..36 |
2919 | paragraphOffset[i+1] = paragraphOffset[i] + paragraph[i].count(); |
2920 | |
2921 | int curParNr = 0; |
2922 | int curParOffset = 0; |
2923 | QMap<QChar,QString> cmap; |
2924 | |
2925 | /* |
2926 | Output the alphabet as a row of links. |
2927 | */ |
2928 | if (includeAlphabet) { |
2929 | writeStartTag(DT_p); |
2930 | xmlWriter().writeAttribute("outputclass", "alphabet"); |
2931 | for (int i = 0; i < 26; i++) { |
2932 | QChar ch( |
2933 | if (usedParagraphNames.contains(char( |
2934 | writeStartTag(DT_xref); |
2935 | // formathtml |
2936 | QString guid = lookupGuid(outFileName(),QString(ch)); |
2937 | QString attr = outFileName() + QString("#%1").arg(guid); |
2938 | xmlWriter().writeAttribute("href", attr); |
2939 | xmlWriter().writeCharacters(QString(ch.toUpper())); |
2940 | writeEndTag(); // </xref> |
2941 | } |
2942 | } |
2943 | writeEndTag(); // </p> |
2944 | } |
2945 | |
2946 | /* |
2947 | Output a <p> element to contain all the <dl> elements. |
2948 | */ |
2949 | writeStartTag(DT_p); |
2950 | xmlWriter().writeAttribute("outputclass", "compactlist"); |
2951 | |
2952 | for (int i=0; i<classMap.count()-1; i++) { |
2953 | while ((curParNr < NumParagraphs) && |
2954 | (curParOffset == paragraph[curParNr].count())) { |
2955 | ++curParNr; |
2956 | curParOffset = 0; |
2957 | } |
2958 | |
2959 | /* |
2960 | Starting a new paragraph means starting a new <dl>. |
2961 | */ |
2962 | if (curParOffset == 0) { |
2963 | if (i > 0) { |
2964 | writeEndTag(); // </dlentry> |
2965 | writeEndTag(); // </dl> |
2966 | } |
2967 | writeStartTag(DT_dl); |
2968 | writeStartTag(DT_dlentry); |
2969 | writeStartTag(DT_dt); |
2970 | if (includeAlphabet) { |
2971 | QChar c = paragraphName[curParNr][0].toLower(); |
2972 | writeGuidAttribute(QString(c)); |
2973 | } |
2974 | xmlWriter().writeAttribute("outputclass", "sublist-header"); |
2975 | xmlWriter().writeCharacters(paragraphName[curParNr]); |
2976 | writeEndTag(); // </dt> |
2977 | } |
2978 | |
2979 | /* |
2980 | Output a <dd> for the current offset in the current paragraph. |
2981 | */ |
2982 | writeStartTag(DT_dd); |
2983 | if ((curParNr < NumParagraphs) && |
2984 | !paragraphName[curParNr].isEmpty()) { |
2985 | NodeMap::Iterator it; |
2986 | it = paragraph[curParNr].begin(); |
2987 | for (int i=0; i<curParOffset; i++) |
2988 | ++it; |
2989 | |
2990 | /* |
2991 | Previously, we used generateFullName() for this, but we |
2992 | require some special formatting. |
2993 | */ |
2994 | writeStartTag(DT_xref); |
2995 | // formathtml |
2996 | xmlWriter().writeAttribute("href",linkForNode(it.value(), relative)); |
2997 | |
2998 | QStringList pieces; |
2999 | if (it.value()->subType() == Node::QmlClass) |
3000 | pieces << it.value()->name(); |
3001 | else |
3002 | pieces = fullName(it.value(), relative, marker).split("::"); |
3003 | xmlWriter().writeCharacters(protectEnc(pieces.last())); |
3004 | writeEndTag(); // </xref> |
3005 | if (pieces.size() > 1) { |
3006 | xmlWriter().writeCharacters(" ("); |
3007 | generateFullName(it.value()->parent(),relative,marker); |
3008 | xmlWriter().writeCharacters(")"); |
3009 | } |
3010 | } |
3011 | writeEndTag(); // </dd> |
3012 | curParOffset++; |
3013 | } |
3014 | writeEndTag(); // </dlentry> |
3015 | writeEndTag(); // </dl> |
3016 | writeEndTag(); // </p> |
3017 | } |
3018 | |
3019 | /*! |
3020 | Write XML for a function index to the current XML stream. |
3021 | */ |
3022 | void DitaXmlGenerator::generateFunctionIndex(const Node* relative, |
3023 | CodeMarker* marker) |
3024 | { |
3025 | writeStartTag(DT_p); |
3026 | xmlWriter().writeAttribute("outputclass", "alphabet"); |
3027 | for (int i = 0; i < 26; i++) { |
3028 | QChar ch( |
3029 | writeStartTag(DT_xref); |
3030 | // formathtml |
3031 | QString guid = lookupGuid(outFileName(),QString(ch)); |
3032 | QString attr = outFileName() + QString("#%1").arg(guid); |
3033 | xmlWriter().writeAttribute("href", attr); |
3034 | xmlWriter().writeCharacters(QString(ch.toUpper())); |
3035 | writeEndTag(); // </xref> |
3036 | |
3037 | } |
3038 | writeEndTag(); // </p> |
3039 | |
3040 | char nextLetter = |
3041 | char currentLetter; |
3042 | |
3043 | writeStartTag(DT_ul); |
3044 | QMap<QString, NodeMap >::ConstIterator f = funcIndex.begin(); |
3045 | while (f != funcIndex.end()) { |
3046 | writeStartTag(DT_li); |
3047 | currentLetter = f.key()[0].unicode(); |
3048 | while (islower(currentLetter) && currentLetter >= nextLetter) { |
3049 | writeStartTag(DT_p); |
3050 | writeGuidAttribute(QString(nextLetter)); |
3051 | xmlWriter().writeAttribute("outputclass", "target"); |
3052 | xmlWriter().writeCharacters(QString(nextLetter)); |
3053 | writeEndTag(); // </p> |
3054 | nextLetter++; |
3055 | } |
3056 | xmlWriter().writeCharacters(protectEnc(f.key())); |
3057 | xmlWriter().writeCharacters(":"); |
3058 | |
3059 | NodeMap::ConstIterator s = (*f).begin(); |
3060 | while (s != (*f).end()) { |
3061 | generateFullName((*s)->parent(), relative, marker, *s); |
3062 | ++s; |
3063 | } |
3064 | writeEndTag(); // </li> |
3065 | ++f; |
3066 | } |
3067 | writeEndTag(); // </ul> |
3068 | } |
3069 | |
3070 | /*! |
3071 | Write the legalese texts as XML to the current XML stream. |
3072 | */ |
3073 | void DitaXmlGenerator::generateLegaleseList(const Node* relative, |
3074 | CodeMarker* marker) |
3075 | { |
3076 | QMap<Text, const Node*>::ConstIterator it = legaleseTexts.begin(); |
3077 | while (it != legaleseTexts.end()) { |
3078 | Text text = it.key(); |
3079 | generateText(text, relative, marker); |
3080 | writeStartTag(DT_ul); |
3081 | do { |
3082 | writeStartTag(DT_li); |
3083 | generateFullName(it.value(), relative, marker); |
3084 | writeEndTag(); // </li> |
3085 | ++it; |
3086 | } while (it != legaleseTexts.end() && it.key() == text); |
3087 | writeEndTag(); //</ul> |
3088 | } |
3089 | } |
3090 | |
3091 | /*! |
3092 | Generate the text for the QML item described by \a node |
3093 | and write it to the current XML stream. |
3094 | */ |
3095 | void DitaXmlGenerator::generateQmlItem(const Node* node, |
3096 | const Node* relative, |
3097 | CodeMarker* marker, |
3098 | bool summary) |
3099 | { |
3100 | QString marked = marker->markedUpQmlItem(node,summary); |
3101 | QRegExp tag("(<[^@>]*>)"); |
3102 | if (marked.indexOf(tag) != -1) { |
3103 | QString tmp = protectEnc(marked.mid(tag.pos(1), tag.cap(1).length())); |
3104 | marked.replace(tag.pos(1), tag.cap(1).length(), tmp); |
3105 | } |
3106 | marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), |
3107 | "<i>\\1<sub>\\2</sub></i>"); |
3108 | marked.replace("<@param>", "<i>"); |
3109 | marked.replace("</@param>", "</i>"); |
3110 | |
3111 | if (summary) |
3112 | marked.replace("@name>", "b>"); |
3113 | |
3114 | marked.replace("<@extra>", "<tt>"); |
3115 | marked.replace("</@extra>", "</tt>"); |
3116 | |
3117 | if (summary) { |
3118 | marked.replace("<@type>", ""); |
3119 | marked.replace("</@type>", ""); |
3120 | } |
3121 | writeText(marked, marker, relative); |
3122 | } |
3123 | |
3124 | /*! |
3125 | Writher the XML for the overview list to the current XML stream. |
3126 | */ |
3127 | void DitaXmlGenerator::generateOverviewList(const Node* relative, CodeMarker* /* marker */) |
3128 | { |
3129 | QMap<const FakeNode*, QMap<QString, FakeNode*> > fakeNodeMap; |
3130 | QMap<QString, const FakeNode*> groupTitlesMap; |
3131 | QMap<QString, FakeNode*> uncategorizedNodeMap; |
3132 | QRegExp singleDigit("\\b([0-9])\\b"); |
3133 | |
3134 | const NodeList children = myTree->root()->childNodes(); |
3135 | foreach (Node* child, children) { |
3136 | if (child->type() == Node::Fake && child != relative) { |
3137 | FakeNode* fakeNode = static_cast<FakeNode*>(child); |
3138 | |
3139 | // Check whether the page is part of a group or is the group |
3140 | // definition page. |
3141 | QString group; |
3142 | bool isGroupPage = false; |
3143 | if (fakeNode->doc().metaCommandsUsed().contains("group")) { |
3144 | group = fakeNode->doc().metaCommandArgs("group")[0]; |
3145 | isGroupPage = true; |
3146 | } |
3147 | |
3148 | // there are too many examples; they would clutter the list |
3149 | if (fakeNode->subType() == Node::Example) |
3150 | continue; |
3151 | |
3152 | // not interested either in individual (Qt Designer etc.) manual chapters |
3153 | if (fakeNode->links().contains(Node::ContentsLink)) |
3154 | continue; |
3155 | |
3156 | // Discard external nodes. |
3157 | if (fakeNode->subType() == Node::ExternalPage) |
3158 | continue; |
3159 | |
3160 | QString sortKey = fakeNode->fullTitle().toLower(); |
3161 | if (sortKey.startsWith("the ")) |
3162 | sortKey.remove(0, 4); |
3163 | sortKey.replace(singleDigit, "0\\1"); |
3164 | |
3165 | if (!group.isEmpty()) { |
3166 | if (isGroupPage) { |
3167 | // If we encounter a group definition page, we add all |
3168 | // the pages in that group to the list for that group. |
3169 | foreach (Node* member, fakeNode->groupMembers()) { |
3170 | if (member->type() != Node::Fake) |
3171 | continue; |
3172 | FakeNode* page = static_cast<FakeNode*>(member); |
3173 | if (page) { |
3174 | QString sortKey = page->fullTitle().toLower(); |
3175 | if (sortKey.startsWith("the ")) |
3176 | sortKey.remove(0, 4); |
3177 | sortKey.replace(singleDigit, "0\\1"); |
3178 | fakeNodeMap[const_cast<const FakeNode*>(fakeNode)].insert(sortKey, page); |
3179 | groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode*>(fakeNode); |
3180 | } |
3181 | } |
3182 | } |
3183 | else if (!isGroupPage) { |
3184 | // If we encounter a page that belongs to a group then |
3185 | // we add that page to the list for that group. |
3186 | const FakeNode* groupNode = |
3187 | static_cast<const FakeNode*>(myTree->root()->findNode(group, Node::Fake)); |
3188 | if (groupNode) |
3189 | fakeNodeMap[groupNode].insert(sortKey, fakeNode); |
3190 | //else |
3191 | // uncategorizedNodeMap.insert(sortKey, fakeNode); |
3192 | }// else |
3193 | // uncategorizedNodeMap.insert(sortKey, fakeNode); |
3194 | }// else |
3195 | // uncategorizedNodeMap.insert(sortKey, fakeNode); |
3196 | } |
3197 | } |
3198 | |
3199 | // We now list all the pages found that belong to groups. |
3200 | // If only certain pages were found for a group, but the definition page |
3201 | // for that group wasn't listed, the list of pages will be intentionally |
3202 | // incomplete. However, if the group definition page was listed, all the |
3203 | // pages in that group are listed for completeness. |
3204 | |
3205 | if (!fakeNodeMap.isEmpty()) { |
3206 | foreach (const QString& groupTitle, groupTitlesMap.keys()) { |
3207 | const FakeNode* groupNode = groupTitlesMap[groupTitle]; |
3208 | writeStartTag(DT_p); |
3209 | xmlWriter().writeAttribute("outputclass", "h3"); |
3210 | writeStartTag(DT_xref); |
3211 | // formathtml |
3212 | xmlWriter().writeAttribute("href",linkForNode(groupNode, relative)); |
3213 | writeCharacters(protectEnc(groupNode->fullTitle())); |
3214 | writeEndTag(); // </xref> |
3215 | writeEndTag(); // </p> |
3216 | if (fakeNodeMap[groupNode].count() == 0) |
3217 | continue; |
3218 | |
3219 | writeStartTag(DT_ul); |
3220 | foreach (const FakeNode* fakeNode, fakeNodeMap[groupNode]) { |
3221 | QString title = fakeNode->fullTitle(); |
3222 | if (title.startsWith("The ")) |
3223 | title.remove(0, 4); |
3224 | writeStartTag(DT_li); |
3225 | writeStartTag(DT_xref); |
3226 | // formathtml |
3227 | xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative)); |
3228 | writeCharacters(protectEnc(title)); |
3229 | writeEndTag(); // </xref> |
3230 | writeEndTag(); // </li> |
3231 | } |
3232 | writeEndTag(); // </ul> |
3233 | } |
3234 | } |
3235 | |
3236 | if (!uncategorizedNodeMap.isEmpty()) { |
3237 | writeStartTag(DT_p); |
3238 | xmlWriter().writeAttribute("outputclass", "h3"); |
3239 | xmlWriter().writeCharacters("Miscellaneous"); |
3240 | writeEndTag(); // </p> |
3241 | writeStartTag(DT_ul); |
3242 | foreach (const FakeNode *fakeNode, uncategorizedNodeMap) { |
3243 | QString title = fakeNode->fullTitle(); |
3244 | if (title.startsWith("The ")) |
3245 | title.remove(0, 4); |
3246 | writeStartTag(DT_li); |
3247 | writeStartTag(DT_xref); |
3248 | // formathtml |
3249 | xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative)); |
3250 | writeCharacters(protectEnc(title)); |
3251 | writeEndTag(); // </xref> |
3252 | writeEndTag(); // </li> |
3253 | } |
3254 | writeEndTag(); // </ul> |
3255 | } |
3256 | } |
3257 | |
3258 | /*! |
3259 | Write the XML for a standard section of a page, e.g. |
3260 | "Public Functions" or "Protected Slots." The section |
3261 | is written too the current XML stream as a table. |
3262 | */ |
3263 | void DitaXmlGenerator::generateSection(const NodeList& nl, |
3264 | const Node* relative, |
3265 | CodeMarker* marker, |
3266 | CodeMarker::SynopsisStyle style) |
3267 | { |
3268 | if (!nl.isEmpty()) { |
3269 | writeStartTag(DT_ul); |
3270 | NodeList::ConstIterator m = nl.begin(); |
3271 | while (m != nl.end()) { |
3272 | if ((*m)->access() != Node::Private) { |
3273 | writeStartTag(DT_li); |
3274 | QString marked = getMarkedUpSynopsis(*m, relative, marker, style); |
3275 | writeText(marked, marker, relative); |
3276 | writeEndTag(); // </li> |
3277 | } |
3278 | ++m; |
3279 | } |
3280 | writeEndTag(); // </ul> |
3281 | } |
3282 | } |
3283 | |
3284 | /*! |
3285 | Writes the "inherited from" list to the current XML stream. |
3286 | */ |
3287 | void DitaXmlGenerator::generateSectionInheritedList(const Section& section, |
3288 | const Node* relative, |
3289 | CodeMarker* marker) |
3290 | { |
3291 | if (section.inherited.isEmpty()) |
3292 | return; |
3293 | writeStartTag(DT_ul); |
3294 | QList<QPair<ClassNode*,int> >::ConstIterator p = section.inherited.begin(); |
3295 | while (p != section.inherited.end()) { |
3296 | writeStartTag(DT_li); |
3297 | QString text; |
3298 | text.setNum((*p).second); |
3299 | text += " "; |
3300 | if ((*p).second == 1) |
3301 | text += section.singularMember; |
3302 | else |
3303 | text += section.pluralMember; |
3304 | text += " inherited from "; |
3305 | writeCharacters(text); |
3306 | writeStartTag(DT_xref); |
3307 | // formathtml |
3308 | // zzz |
3309 | text = fileName((*p).first) + "#"; |
3310 | text += DitaXmlGenerator::cleanRef(section.name.toLower()); |
3311 | xmlWriter().writeAttribute("href",text); |
3312 | text = protectEnc(marker->plainFullName((*p).first, relative)); |
3313 | writeCharacters(text); |
3314 | writeEndTag(); // </xref> |
3315 | writeEndTag(); // </li> |
3316 | ++p; |
3317 | } |
3318 | writeEndTag(); // </ul> |
3319 | } |
3320 | |
3321 | /*! |
3322 | Get the synopsis from the \a node using the \a relative |
3323 | node if needed, and mark up the synopsis using \a marker. |
3324 | Use the style to decide which kind of sysnopsis to build, |
3325 | normally \c Summary or \c Detailed. Return the marked up |
3326 | string. |
3327 | */ |
3328 | QString DitaXmlGenerator::getMarkedUpSynopsis(const Node* node, |
3329 | const Node* relative, |
3330 | CodeMarker* marker, |
3331 | CodeMarker::SynopsisStyle style) |
3332 | { |
3333 | QString marked = marker->markedUpSynopsis(node, relative, style); |
3334 | QRegExp tag("(<[^@>]*>)"); |
3335 | if (marked.indexOf(tag) != -1) { |
3336 | QString tmp = protectEnc(marked.mid(tag.pos(1), tag.cap(1).length())); |
3337 | marked.replace(tag.pos(1), tag.cap(1).length(), tmp); |
3338 | } |
3339 | marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), |
3340 | "<i>\\1<sub>\\2</sub></i>"); |
3341 | #if 0 |
3342 | marked.replace("<@param>", "<i>"); |
3343 | marked.replace("</@param>", "</i>"); |
3344 | #endif |
3345 | if (style == CodeMarker::Summary) { |
3346 | marked.replace("<@name>", ""); // was "<b>" |
3347 | marked.replace("</@name>", ""); // was "</b>" |
3348 | } |
3349 | |
3350 | if (style == CodeMarker::SeparateList) { |
3351 | QRegExp extraRegExp("<@extra>.*</@extra>"); |
3352 | extraRegExp.setMinimal(true); |
3353 | marked.replace(extraRegExp,""); |
3354 | } |
3355 | #if 0 |
3356 | else { |
3357 | marked.replace("<@extra>", "<tt>"); |
3358 | marked.replace("</@extra>", "</tt>"); |
3359 | } |
3360 | #endif |
3361 | |
3362 | if (style != CodeMarker::Detailed) { |
3363 | marked.replace("<@type>", ""); |
3364 | marked.replace("</@type>", ""); |
3365 | } |
3366 | return marked; |
3367 | } |
3368 | |
3369 | /*! |
3370 | Renamed from highlightedCode() in the html generator. Writes |
3371 | the \a markedCode to the current XML stream. |
3372 | */ |
3373 | void DitaXmlGenerator::writeText(const QString& markedCode, |
3374 | CodeMarker* marker, |
3375 | const Node* relative) |
3376 | { |
3377 | QString src = markedCode; |
3378 | QString html; |
3379 | QStringRef arg; |
3380 | QStringRef par1; |
3381 | |
3382 | const QChar charLangle = |
3383 | const QChar charAt = |
3384 | |
3385 | /* |
3386 | First strip out all the extraneous markup. The table |
3387 | below contains the markup we want to keep. Everything |
3388 | else that begins with "<@" or "</@" is stripped out. |
3389 | */ |
3390 | static const QString spanTags[] = { |
3391 | "<@link ", "<@link ", |
3392 | "<@type>", "<@type>", |
3393 | "<@headerfile>", "<@headerfile>", |
3394 | "<@func>", "<@func>", |
3395 | "<@func ", "<@func ", |
3396 | "<@param>", "<@param>", |
3397 | "<@extra>", "<@extra>", |
3398 | "</@link>", "</@link>", |
3399 | "</@type>", "</@type>", |
3400 | "</@headerfile>", "</@headerfile>", |
3401 | "</@func>", "</@func>", |
3402 | "</@param>", "</@param>", |
3403 | "</@extra>", "</@extra>" |
3404 | }; |
3405 | for (int i = 0, n = src.size(); i < n;) { |
3406 | if (src.at(i) == charLangle) { |
3407 | bool handled = false; |
3408 | for (int k = 0; k != 13; ++k) { |
3409 | const QString & tag = spanTags[2 * k]; |
3410 | if (tag == QStringRef(&src, i, tag.length())) { |
3411 | html += spanTags[2 * k + 1]; |
3412 | i += tag.length(); |
3413 | handled = true; |
3414 | break; |
3415 | } |
3416 | } |
3417 | if (!handled) { |
3418 | ++i; |
3419 | if (src.at(i) == charAt || |
3420 | (src.at(i) == QLatin1Char( |
3421 | // drop 'our' unknown tags (the ones still containing '@') |
3422 | while (i < n && src.at(i) != QLatin1Char( |
3423 | ++i; |
3424 | ++i; |
3425 | } |
3426 | else { |
3427 | // retain all others |
3428 | html += charLangle; |
3429 | } |
3430 | } |
3431 | } |
3432 | else { |
3433 | html += src.at(i); |
3434 | ++i; |
3435 | } |
3436 | } |
3437 | |
3438 | // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)" |
3439 | // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags |
3440 | src = html; |
3441 | html = QString(); |
3442 | static const QString markTags[] = { |
3443 | // 0 1 2 3 4 5 |
3444 | "link", "type", "headerfile", "func", "param", "extra" |
3445 | }; |
3446 | |
3447 | for (int i = 0, n = src.size(); i < n;) { |
3448 | if (src.at(i) == charLangle && src.at(i + 1) == charAt) { |
3449 | i += 2; |
3450 | for (int k = 0; k != 6; ++k) { |
3451 | if (parseArg(src, markTags[k], &i, n, &arg, &par1)) { |
3452 | const Node* n = 0; |
3453 | if (k == 0) { // <@link> |
3454 | if (!html.isEmpty()) { |
3455 | writeCharacters(html); |
3456 | html.clear(); |
3457 | } |
3458 | n = CodeMarker::nodeForString(par1.toString()); |
3459 | QString link = linkForNode(n, relative); |
3460 | addLink(link, arg); |
3461 | } |
3462 | else if (k == 4) { // <@param> |
3463 | if (!html.isEmpty()) { |
3464 | writeCharacters(html); |
3465 | html.clear(); |
3466 | } |
3467 | writeStartTag(DT_i); |
3468 | writeCharacters(arg.toString()); |
3469 | writeEndTag(); // </i> |
3470 | } |
3471 | else if (k == 5) { // <@extra> |
3472 | if (!html.isEmpty()) { |
3473 | writeCharacters(html); |
3474 | html.clear(); |
3475 | } |
3476 | writeStartTag(DT_tt); |
3477 | writeCharacters(arg.toString()); |
3478 | writeEndTag(); // </tt> |
3479 | } |
3480 | else { |
3481 | if (!html.isEmpty()) { |
3482 | writeCharacters(html); |
3483 | html.clear(); |
3484 | } |
3485 | par1 = QStringRef(); |
3486 | QString link; |
3487 | n = marker->resolveTarget(arg.toString(), myTree, relative); |
3488 | if (n && n->subType() == Node::QmlBasicType) { |
3489 | if (relative && relative->subType() == Node::QmlClass) { |
3490 | link = linkForNode(n,relative); |
3491 | addLink(link, arg); |
3492 | } |
3493 | else { |
3494 | //Encountered in snippets for example. Where the text should not be a link. |
3495 | writeCharacters(arg.toString()); |
3496 | } |
3497 | } |
3498 | else { |
3499 | // (zzz) Is this correct for all cases? |
3500 | link = linkForNode(n,relative); |
3501 | addLink(link, arg); |
3502 | } |
3503 | } |
3504 | break; |
3505 | } |
3506 | } |
3507 | } |
3508 | else { |
3509 | html += src.at(i++); |
3510 | } |
3511 | } |
3512 | |
3513 | if (!html.isEmpty()) { |
3514 | writeCharacters(html); |
3515 | } |
3516 | } |
3517 | |
3518 | void DitaXmlGenerator::generateLink(const Atom* atom, |
3519 | const Node* /* relative */, |
3520 | CodeMarker* marker) |
3521 | { |
3522 | static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_"); |
3523 | |
3524 | if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) { |
3525 | // hack for C++: move () outside of link |
3526 | int k = funcLeftParen.pos(1); |
3527 | writeCharacters(protectEnc(atom->string().left(k))); |
3528 | if (link.isEmpty()) { |
3529 | if (showBrokenLinks) |
3530 | writeEndTag(); // </i> |
3531 | } |
3532 | else |
3533 | writeEndTag(); // </xref> |
3534 | inLink = false; |
3535 | writeCharacters(protectEnc(atom->string().mid(k))); |
3536 | } |
3537 | else if (marker->recognizeLanguage("Java")) { |
3538 | // hack for Java: remove () and use <tt> when appropriate |
3539 | bool func = atom->string().endsWith("()"); |
3540 | bool tt = (func || atom->string().contains(camelCase)); |
3541 | if (tt) |
3542 | writeStartTag(DT_tt); |
3543 | if (func) |
3544 | writeCharacters(protectEnc(atom->string().left(atom->string().length() - 2))); |
3545 | else |
3546 | writeCharacters(protectEnc(atom->string())); |
3547 | writeEndTag(); // </tt> |
3548 | } |
3549 | else |
3550 | writeCharacters(protectEnc(atom->string())); |
3551 | } |
3552 | |
3553 | QString DitaXmlGenerator::cleanRef(const QString& ref) |
3554 | { |
3555 | QString clean; |
3556 | |
3557 | if (ref.isEmpty()) |
3558 | return clean; |
3559 | |
3560 | clean.reserve(ref.size() + 20); |
3561 | const QChar c = ref[0]; |
3562 | const uint u = c.unicode(); |
3563 | |
3564 | if ((u >= |
3565 | (u >= |
3566 | (u >= |
3567 | clean += c; |
3568 | } |
3569 | else if (u == |
3570 | clean += "dtor."; |
3571 | } |
3572 | else if (u == |
3573 | clean += "underscore."; |
3574 | } |
3575 | else { |
3576 | clean += "A"; |
3577 | } |
3578 | |
3579 | for (int i = 1; i < (int) ref.length(); i++) { |
3580 | const QChar c = ref[i]; |
3581 | const uint u = c.unicode(); |
3582 | if ((u >= |
3583 | (u >= |
3584 | (u >= |
3585 | u == |
3586 | clean += c; |
3587 | } |
3588 | else if (c.isSpace()) { |
3589 | clean += "-"; |
3590 | } |
3591 | else if (u == |
3592 | clean += "-not"; |
3593 | } |
3594 | else if (u == |
3595 | clean += "-and"; |
3596 | } |
3597 | else if (u == |
3598 | clean += "-lt"; |
3599 | } |
3600 | else if (u == |
3601 | clean += "-eq"; |
3602 | } |
3603 | else if (u == |
3604 | clean += "-gt"; |
3605 | } |
3606 | else if (u == |
3607 | clean += "#"; |
3608 | } |
3609 | else { |
3610 | clean += "-"; |
3611 | clean += QString::number((int)u, 16); |
3612 | } |
3613 | } |
3614 | return clean; |
3615 | } |
3616 | |
3617 | QString DitaXmlGenerator::registerRef(const QString& ref) |
3618 | { |
3619 | QString clean = DitaXmlGenerator::cleanRef(ref); |
3620 | |
3621 | for (;;) { |
3622 | QString& prevRef = refMap[clean.toLower()]; |
3623 | if (prevRef.isEmpty()) { |
3624 | prevRef = ref; |
3625 | break; |
3626 | } |
3627 | else if (prevRef == ref) |
3628 | break; |
3629 | clean += "x"; |
3630 | } |
3631 | return clean; |
3632 | } |
3633 | |
3634 | /*! |
3635 | Calls protect() with the \a string. Returns the result. |
3636 | */ |
3637 | QString DitaXmlGenerator::protectEnc(const QString& string) |
3638 | { |
3639 | return protect(string, outputEncoding); |
3640 | } |
3641 | |
3642 | QString DitaXmlGenerator::protect(const QString& string, const QString& ) //outputEncoding) |
3643 | { |
3644 | #define APPEND(x) \ |
3645 | if (xml.isEmpty()) { \ |
3646 | xml = string; \ |
3647 | xml.truncate(i); \ |
3648 | } \ |
3649 | xml += (x); |
3650 | |
3651 | QString xml; |
3652 | int n = string.length(); |
3653 | |
3654 | for (int i = 0; i < n; ++i) { |
3655 | QChar ch = string.at(i); |
3656 | |
3657 | if (ch == QLatin1Char( |
3658 | APPEND("&"); |
3659 | } |
3660 | else if (ch == QLatin1Char( |
3661 | APPEND("<"); |
3662 | } |
3663 | else if (ch == QLatin1Char( |
3664 | APPEND(">"); |
3665 | } |
3666 | else if (ch == QLatin1Char( |
3667 | APPEND("""); |
3668 | } |
3669 | #if 0 |
3670 | else if ((outputEncoding == "ISO-8859-1"&& ch.unicode() > 0x007F) || |
3671 | (ch == QLatin1Char( |
3672 | (ch == QLatin1Char( |
3673 | // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator |
3674 | APPEND("&#x"); |
3675 | xml += QString::number(ch.unicode(), 16); |
3676 | xml += QLatin1Char( |
3677 | } |
3678 | #endif |
3679 | else { |
3680 | if (!xml.isEmpty()) |
3681 | xml += ch; |
3682 | } |
3683 | } |
3684 | |
3685 | if (!xml.isEmpty()) |
3686 | return xml; |
3687 | return string; |
3688 | |
3689 | #undef APPEND |
3690 | } |
3691 | |
3692 | /*! |
3693 | Constructs a file name appropriate for the \a node |
3694 | and returns the file name. |
3695 | */ |
3696 | QString DitaXmlGenerator::fileBase(const Node* node) const |
3697 | { |
3698 | QString result; |
3699 | result = PageGenerator::fileBase(node); |
3700 | #if 0 |
3701 | if (!node->isInnerNode()) { |
3702 | switch (node->status()) { |
3703 | case Node::Compat: |
3704 | result += "-qt3"; |
3705 | break; |
3706 | case Node::Obsolete: |
3707 | result += "-obsolete"; |
3708 | break; |
3709 | default: |
3710 | ; |
3711 | } |
3712 | } |
3713 | #endif |
3714 | return result; |
3715 | } |
3716 | |
3717 | QString DitaXmlGenerator::refForNode(const Node* node) |
3718 | { |
3719 | const FunctionNode* func; |
3720 | const TypedefNode* tdn; |
3721 | QString ref; |
3722 | |
3723 | switch (node->type()) { |
3724 | case Node::Namespace: |
3725 | case Node::Class: |
3726 | default: |
3727 | break; |
3728 | case Node::Enum: |
3729 | ref = node->name() + "-enum"; |
3730 | break; |
3731 | case Node::Typedef: |
3732 | tdn = static_cast<const TypedefNode *>(node); |
3733 | if (tdn->associatedEnum()) { |
3734 | return refForNode(tdn->associatedEnum()); |
3735 | } |
3736 | else { |
3737 | ref = node->name() + "-typedef"; |
3738 | } |
3739 | break; |
3740 | case Node::Function: |
3741 | func = static_cast<const FunctionNode *>(node); |
3742 | if (func->associatedProperty()) { |
3743 | return refForNode(func->associatedProperty()); |
3744 | } |
3745 | else { |
3746 | ref = func->name(); |
3747 | if (func->overloadNumber() != 1) |
3748 | ref += "-"+ QString::number(func->overloadNumber()); |
3749 | } |
3750 | break; |
3751 | case Node::Fake: |
3752 | if (node->subType() != Node::QmlPropertyGroup) |
3753 | break; |
3754 | case Node::QmlProperty: |
3755 | case Node::Property: |
3756 | ref = node->name() + "-prop"; |
3757 | break; |
3758 | case Node::QmlSignal: |
3759 | ref = node->name() + "-signal"; |
3760 | break; |
3761 | case Node::QmlMethod: |
3762 | ref = node->name() + "-method"; |
3763 | break; |
3764 | case Node::Variable: |
3765 | ref = node->name() + "-var"; |
3766 | break; |
3767 | case Node::Target: |
3768 | return protectEnc(node->name()); |
3769 | } |
3770 | return registerRef(ref); |
3771 | } |
3772 | |
3773 | QString DitaXmlGenerator::guidForNode(const Node* node) |
3774 | { |
3775 | switch (node->type()) { |
3776 | case Node::Namespace: |
3777 | case Node::Class: |
3778 | default: |
3779 | break; |
3780 | case Node::Enum: |
3781 | return node->guid(); |
3782 | case Node::Typedef: |
3783 | { |
3784 | const TypedefNode* tdn = static_cast<const TypedefNode*>(node); |
3785 | if (tdn->associatedEnum()) |
3786 | return guidForNode(tdn->associatedEnum()); |
3787 | } |
3788 | return node->guid(); |
3789 | case Node::Function: |
3790 | { |
3791 | const FunctionNode* fn = static_cast<const FunctionNode*>(node); |
3792 | if (fn->associatedProperty()) { |
3793 | return guidForNode(fn->associatedProperty()); |
3794 | } |
3795 | else { |
3796 | QString ref = fn->name(); |
3797 | if (fn->overloadNumber() != 1) { |
3798 | ref += "-"+ QString::number(fn->overloadNumber()); |
3799 | } |
3800 | } |
3801 | return fn->guid(); |
3802 | } |
3803 | case Node::Fake: |
3804 | if (node->subType() != Node::QmlPropertyGroup) |
3805 | break; |
3806 | case Node::QmlProperty: |
3807 | case Node::Property: |
3808 | return node->guid(); |
3809 | case Node::QmlSignal: |
3810 | return node->guid(); |
3811 | case Node::QmlMethod: |
3812 | return node->guid(); |
3813 | case Node::Variable: |
3814 | return node->guid(); |
3815 | case Node::Target: |
3816 | return node->guid(); |
3817 | } |
3818 | return QString(); |
3819 | } |
3820 | |
3821 | /*! |
3822 | Constructs a file name appropriate for the \a node and returns |
3823 | it. If the \a node is not a fake node, or if it is a fake node but |
3824 | it is neither an external page node nor an image node, call the |
3825 | PageGenerator::fileName() function. |
3826 | */ |
3827 | QString DitaXmlGenerator::fileName(const Node* node) |
3828 | { |
3829 | if (node->type() == Node::Fake) { |
3830 | if (static_cast<const FakeNode*>(node)->subType() == Node::ExternalPage) |
3831 | return node->name(); |
3832 | if (static_cast<const FakeNode*>(node)->subType() == Node::Image) |
3833 | return node->name(); |
3834 | } |
3835 | return PageGenerator::fileName(node); |
3836 | } |
3837 | |
3838 | QString DitaXmlGenerator::linkForNode(const Node* node, const Node* relative) |
3839 | { |
3840 | if (node == 0 || node == relative) |
3841 | return QString(); |
3842 | if (!node->url().isEmpty()) |
3843 | return node->url(); |
3844 | if (fileBase(node).isEmpty()) |
3845 | return QString(); |
3846 | if (node->access() == Node::Private) |
3847 | return QString(); |
3848 | |
3849 | QString fn = fileName(node); |
3850 | QString link = fn; |
3851 | |
3852 | if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) { |
3853 | QString guid = guidForNode(node); |
3854 | if (relative && fn == fileName(relative) && guid == guidForNode(relative)) { |
3855 | return QString(); |
3856 | } |
3857 | link += "#"; |
3858 | link += guid; |
3859 | } |
3860 | return link; |
3861 | } |
3862 | |
3863 | QString DitaXmlGenerator::refForAtom(Atom* atom, const Node* /* node */) |
3864 | { |
3865 | if (atom->type() == Atom::SectionLeft) |
3866 | return Doc::canonicalTitle(Text::sectionHeading(atom).toString()); |
3867 | if (atom->type() == Atom::Target) |
3868 | return Doc::canonicalTitle(atom->string()); |
3869 | return QString(); |
3870 | } |
3871 | |
3872 | void DitaXmlGenerator::generateFullName(const Node* apparentNode, |
3873 | const Node* relative, |
3874 | CodeMarker* marker, |
3875 | const Node* actualNode) |
3876 | { |
3877 | if (actualNode == 0) |
3878 | actualNode = apparentNode; |
3879 | writeStartTag(DT_xref); |
3880 | // formathtml |
3881 | QString href = linkForNode(actualNode, relative); |
3882 | xmlWriter().writeAttribute("href",href); |
3883 | writeCharacters(protectEnc(fullName(apparentNode, relative, marker))); |
3884 | writeEndTag(); // </xref> |
3885 | } |
3886 | |
3887 | void DitaXmlGenerator::findAllClasses(const InnerNode* node) |
3888 | { |
3889 | NodeList::const_iterator c = node->childNodes().constBegin(); |
3890 | while (c != node->childNodes().constEnd()) { |
3891 | if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) { |
3892 | if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) { |
3893 | QString className = (*c)->name(); |
3894 | if ((*c)->parent() && |
3895 | (*c)->parent()->type() == Node::Namespace && |
3896 | !(*c)->parent()->name().isEmpty()) |
3897 | className = (*c)->parent()->name()+"::"+className; |
3898 | |
3899 | if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) { |
3900 | if ((*c)->status() == Node::Compat) { |
3901 | compatClasses.insert(className, *c); |
3902 | } |
3903 | else if ((*c)->status() == Node::Obsolete) { |
3904 | obsoleteClasses.insert(className, *c); |
3905 | } |
3906 | else { |
3907 | nonCompatClasses.insert(className, *c); |
3908 | if ((*c)->status() == Node::Main) |
3909 | mainClasses.insert(className, *c); |
3910 | } |
3911 | } |
3912 | |
3913 | QString moduleName = (*c)->moduleName(); |
3914 | if (moduleName == "Qt3SupportLight") { |
3915 | moduleClassMap[moduleName].insert((*c)->name(), *c); |
3916 | moduleName = "Qt3Support"; |
3917 | } |
3918 | if (!moduleName.isEmpty()) |
3919 | moduleClassMap[moduleName].insert((*c)->name(), *c); |
3920 | |
3921 | QString serviceName = |
3922 | (static_cast<const ClassNode *>(*c))->serviceName(); |
3923 | if (!serviceName.isEmpty()) |
3924 | serviceClasses.insert(serviceName, *c); |
3925 | } |
3926 | else if ((*c)->type() == Node::Fake && |
3927 | (*c)->subType() == Node::QmlClass && |
3928 | !(*c)->doc().isEmpty()) { |
3929 | QString qmlClassName = (*c)->name(); |
3930 | qmlClasses.insert(qmlClassName,*c); |
3931 | } |
3932 | else if ((*c)->isInnerNode()) { |
3933 | findAllClasses(static_cast<InnerNode *>(*c)); |
3934 | } |
3935 | } |
3936 | ++c; |
3937 | } |
3938 | } |
3939 | |
3940 | void DitaXmlGenerator::findAllFunctions(const InnerNode* node) |
3941 | { |
3942 | NodeList::ConstIterator c = node->childNodes().begin(); |
3943 | while (c != node->childNodes().end()) { |
3944 | if ((*c)->access() != Node::Private) { |
3945 | if ((*c)->isInnerNode() && (*c)->url().isEmpty()) { |
3946 | findAllFunctions(static_cast<const InnerNode*>(*c)); |
3947 | } |
3948 | else if ((*c)->type() == Node::Function) { |
3949 | const FunctionNode* func = static_cast<const FunctionNode*>(*c); |
3950 | if ((func->status() > Node::Obsolete) && |
3951 | !func->isInternal() && |
3952 | (func->metaness() != FunctionNode::Ctor) && |
3953 | (func->metaness() != FunctionNode::Dtor)) { |
3954 | funcIndex[(*c)->name()].insert(myTree->fullDocumentName((*c)->parent()), *c); |
3955 | } |
3956 | } |
3957 | } |
3958 | ++c; |
3959 | } |
3960 | } |
3961 | |
3962 | void DitaXmlGenerator::findAllLegaleseTexts(const InnerNode* node) |
3963 | { |
3964 | NodeList::ConstIterator c = node->childNodes().begin(); |
3965 | while (c != node->childNodes().end()) { |
3966 | if ((*c)->access() != Node::Private) { |
3967 | if (!(*c)->doc().legaleseText().isEmpty()) |
3968 | legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c); |
3969 | if ((*c)->isInnerNode()) |
3970 | findAllLegaleseTexts(static_cast<const InnerNode *>(*c)); |
3971 | } |
3972 | ++c; |
3973 | } |
3974 | } |
3975 | |
3976 | void DitaXmlGenerator::findAllNamespaces(const InnerNode* node) |
3977 | { |
3978 | NodeList::ConstIterator c = node->childNodes().begin(); |
3979 | while (c != node->childNodes().end()) { |
3980 | if ((*c)->access() != Node::Private) { |
3981 | if ((*c)->isInnerNode() && (*c)->url().isEmpty()) { |
3982 | findAllNamespaces(static_cast<const InnerNode *>(*c)); |
3983 | if ((*c)->type() == Node::Namespace) { |
3984 | const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c); |
3985 | // Ensure that the namespace's name is not empty (the root |
3986 | // namespace has no name). |
3987 | if (!nspace->name().isEmpty()) { |
3988 | namespaceIndex.insert(nspace->name(), *c); |
3989 | QString moduleName = (*c)->moduleName(); |
3990 | if (moduleName == "Qt3SupportLight") { |
3991 | moduleNamespaceMap[moduleName].insert((*c)->name(), *c); |
3992 | moduleName = "Qt3Support"; |
3993 | } |
3994 | if (!moduleName.isEmpty()) |
3995 | moduleNamespaceMap[moduleName].insert((*c)->name(), *c); |
3996 | } |
3997 | } |
3998 | } |
3999 | } |
4000 | ++c; |
4001 | } |
4002 | } |
4003 | |
4004 | /*! |
4005 | We're writing an attribute that indicates that the text |
4006 | data is a heading, hence, h1, h2, h3... etc, and we must |
4007 | decide which number to use. |
4008 | */ |
4009 | int DitaXmlGenerator::hOffset(const Node* node) |
4010 | { |
4011 | switch (node->type()) { |
4012 | case Node::Namespace: |
4013 | case Node::Class: |
4014 | return 2; |
4015 | case Node::Fake: |
4016 | return 1; |
4017 | case Node::Enum: |
4018 | case Node::Typedef: |
4019 | case Node::Function: |
4020 | case Node::Property: |
4021 | default: |
4022 | return 3; |
4023 | } |
4024 | } |
4025 | |
4026 | bool DitaXmlGenerator::isThreeColumnEnumValueTable(const Atom* atom) |
4027 | { |
4028 | while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) { |
4029 | if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight)) |
4030 | return true; |
4031 | atom = atom->next(); |
4032 | } |
4033 | return false; |
4034 | } |
4035 | |
4036 | const Node* DitaXmlGenerator::findNodeForTarget(const QString& target, |
4037 | const Node* relative, |
4038 | CodeMarker* marker, |
4039 | const Atom* atom) |
4040 | { |
4041 | const Node* node = 0; |
4042 | |
4043 | if (target.isEmpty()) { |
4044 | node = relative; |
4045 | } |
4046 | else if (target.endsWith(".html")) { |
4047 | node = myTree->root()->findNode(target, Node::Fake); |
4048 | } |
4049 | else if (marker) { |
4050 | node = marker->resolveTarget(target, myTree, relative); |
4051 | if (!node) |
4052 | node = myTree->findFakeNodeByTitle(target); |
4053 | if (!node && atom) { |
4054 | node = myTree->findUnambiguousTarget(target, |
4055 | *const_cast<Atom**>(&atom)); |
4056 | } |
4057 | } |
4058 | |
4059 | if (!node) |
4060 | relative->doc().location().warning(tr("Cannot link to '%1'").arg(target)); |
4061 | |
4062 | return node; |
4063 | } |
4064 | |
4065 | const QPair<QString,QString> DitaXmlGenerator::anchorForNode(const Node* node) |
4066 | { |
4067 | QPair<QString,QString> anchorPair; |
4068 | anchorPair.first = PageGenerator::fileName(node); |
4069 | if (node->type() == Node::Fake) { |
4070 | const FakeNode *fakeNode = static_cast<const FakeNode*>(node); |
4071 | anchorPair.second = fakeNode->title(); |
4072 | } |
4073 | |
4074 | return anchorPair; |
4075 | } |
4076 | |
4077 | QString DitaXmlGenerator::getLink(const Atom* atom, |
4078 | const Node* relative, |
4079 | CodeMarker* marker, |
4080 | const Node** node) |
4081 | { |
4082 | QString link; |
4083 | *node = 0; |
4084 | inObsoleteLink = false; |
4085 | |
4086 | if (atom->string().contains(":") && |
4087 | (atom->string().startsWith("file:") |
4088 | || atom->string().startsWith("http:") |
4089 | || atom->string().startsWith("https:") |
4090 | || atom->string().startsWith("ftp:") |
4091 | || atom->string().startsWith("mailto:"))) { |
4092 | |
4093 | link = atom->string(); |
4094 | } |
4095 | else { |
4096 | QStringList path; |
4097 | if (atom->string().contains( |
4098 | path = atom->string().split( |
4099 | else |
4100 | path.append(atom->string()); |
4101 | |
4102 | Atom* targetAtom = 0; |
4103 | QString first = path.first().trimmed(); |
4104 | |
4105 | if (first.isEmpty()) { |
4106 | *node = relative; |
4107 | } |
4108 | else if (first.endsWith(".html")) { |
4109 | *node = myTree->root()->findNode(first, Node::Fake); |
4110 | } |
4111 | else { |
4112 | *node = marker->resolveTarget(first, myTree, relative); |
4113 | if (!*node) { |
4114 | *node = myTree->findFakeNodeByTitle(first); |
4115 | } |
4116 | if (!*node) { |
4117 | *node = myTree->findUnambiguousTarget(first, targetAtom); |
4118 | } |
4119 | } |
4120 | |
4121 | if (*node) { |
4122 | if (!(*node)->url().isEmpty()) { |
4123 | return (*node)->url(); |
4124 | } |
4125 | else { |
4126 | path.removeFirst(); |
4127 | } |
4128 | } |
4129 | else { |
4130 | *node = relative; |
4131 | } |
4132 | |
4133 | if (*node && (*node)->status() == Node::Obsolete) { |
4134 | if (relative && (relative->parent() != *node) && |
4135 | (relative->status() != Node::Obsolete)) { |
4136 | bool porting = false; |
4137 | if (relative->type() == Node::Fake) { |
4138 | const FakeNode* fake = static_cast<const FakeNode*>(relative); |
4139 | if (fake->title().startsWith("Porting")) |
4140 | porting = true; |
4141 | } |
4142 | QString name = marker->plainFullName(relative); |
4143 | if (!porting && !name.startsWith("Q3")) { |
4144 | if (obsoleteLinks) { |
4145 | relative->doc().location().warning(tr("Link to obsolete item '%1' in %2") |
4146 | .arg(atom->string()) |
4147 | .arg(name)); |
4148 | } |
4149 | inObsoleteLink = true; |
4150 | } |
4151 | } |
4152 | } |
4153 | |
4154 | while (!path.isEmpty()) { |
4155 | targetAtom = myTree->findTarget(path.first(), *node); |
4156 | if (targetAtom == 0) |
4157 | break; |
4158 | path.removeFirst(); |
4159 | } |
4160 | |
4161 | if (path.isEmpty()) { |
4162 | link = linkForNode(*node, relative); |
4163 | if (*node && (*node)->subType() == Node::Image) |
4164 | link = "images/used-in-examples/"+ link; |
4165 | if (targetAtom) { |
4166 | if (link.isEmpty()) |
4167 | link = outFileName(); |
4168 | QString guid = lookupGuid(link,refForAtom(targetAtom,*node)); |
4169 | link += "#"+ guid; |
4170 | } |
4171 | #if 0 |
4172 | else if (link.isEmpty() && *node) { |
4173 | link = outFileName() + "#"+ (*node)->guid(); |
4174 | } |
4175 | #endif |
4176 | else if (!link.isEmpty() && *node && link.endsWith(".xml")) { |
4177 | link += "#"+ (*node)->guid(); |
4178 | } |
4179 | } |
4180 | } |
4181 | if (!link.isEmpty() && link[0] == |
4182 | link.prepend(outFileName()); |
4183 | qDebug() << "LOCAL LINK:"<< link; |
4184 | } |
4185 | return link; |
4186 | } |
4187 | |
4188 | void DitaXmlGenerator::generateIndex(const QString& fileBase, |
4189 | const QString& url, |
4190 | const QString& title) |
4191 | { |
4192 | myTree->generateIndex(outputDir() + "/"+ fileBase + ".index", url, title); |
4193 | } |
4194 | |
4195 | void DitaXmlGenerator::generateStatus(const Node* node, CodeMarker* marker) |
4196 | { |
4197 | Text text; |
4198 | |
4199 | switch (node->status()) { |
4200 | case Node::Obsolete: |
4201 | if (node->isInnerNode()) |
4202 | Generator::generateStatus(node, marker); |
4203 | break; |
4204 | case Node::Compat: |
4205 | if (node->isInnerNode()) { |
4206 | text << Atom::ParaLeft |
4207 | << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) |
4208 | << "This " |
4209 | << typeString(node) |
4210 | << " is part of the Qt 3 support library." |
4211 | << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) |
4212 | << " It is provided to keep old source code working. " |
4213 | << "We strongly advise against " |
4214 | << "using it in new code. See "; |
4215 | |
4216 | const FakeNode *fakeNode = myTree->findFakeNodeByTitle("Porting To Qt 4"); |
4217 | Atom *targetAtom = 0; |
4218 | if (fakeNode && node->type() == Node::Class) { |
4219 | QString oldName(node->name()); |
4220 | targetAtom = myTree->findTarget(oldName.replace("3", ""),fakeNode); |
4221 | } |
4222 | |
4223 | if (targetAtom) { |
4224 | QString fn = fileName(fakeNode); |
4225 | QString guid = lookupGuid(fn,refForAtom(targetAtom,fakeNode)); |
4226 | text << Atom(Atom::GuidLink, fn + "#"+ guid); |
4227 | } |
4228 | else |
4229 | text << Atom(Atom::Link, "Porting to Qt 4"); |
4230 | |
4231 | text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) |
4232 | << Atom(Atom::String, "Porting to Qt 4") |
4233 | << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) |
4234 | << " for more information." |
4235 | << Atom::ParaRight; |
4236 | } |
4237 | generateText(text, node, marker); |
4238 | break; |
4239 | default: |
4240 | Generator::generateStatus(node, marker); |
4241 | } |
4242 | } |
4243 | |
4244 | void DitaXmlGenerator::beginLink(const QString& link) |
4245 | { |
4246 | this->link = link; |
4247 | if (link.isEmpty()) |
4248 | return; |
4249 | writeStartTag(DT_xref); |
4250 | // formathtml |
4251 | xmlWriter().writeAttribute("href",link); |
4252 | inLink = true; |
4253 | } |
4254 | |
4255 | void DitaXmlGenerator::endLink() |
4256 | { |
4257 | if (inLink) { |
4258 | if (link.isEmpty()) { |
4259 | if (showBrokenLinks) |
4260 | writeEndTag(); // </i> |
4261 | } |
4262 | else { |
4263 | if (inObsoleteLink) { |
4264 | writeStartTag(DT_sup); |
4265 | xmlWriter().writeCharacters("(obsolete)"); |
4266 | writeEndTag(); // </sup> |
4267 | } |
4268 | writeEndTag(); // </xref> |
4269 | } |
4270 | } |
4271 | inLink = false; |
4272 | inObsoleteLink = false; |
4273 | } |
4274 | |
4275 | /*! |
4276 | Generates the summary for the \a section. Only used for |
4277 | sections of QML element documentation. |
4278 | |
4279 | Currently handles only the QML property group. |
4280 | */ |
4281 | void DitaXmlGenerator::generateQmlSummary(const Section& section, |
4282 | const Node* relative, |
4283 | CodeMarker* marker) |
4284 | { |
4285 | if (!section.members.isEmpty()) { |
4286 | writeStartTag(DT_ul); |
4287 | NodeList::ConstIterator m; |
4288 | m = section.members.begin(); |
4289 | while (m != section.members.end()) { |
4290 | writeStartTag(DT_li); |
4291 | generateQmlItem(*m,relative,marker,true); |
4292 | writeEndTag(); // </li> |
4293 | ++m; |
4294 | } |
4295 | writeEndTag(); // </ul> |
4296 | } |
4297 | } |
4298 | |
4299 | /*! |
4300 | zzz |
4301 | Outputs the html detailed documentation for a section |
4302 | on a QML element reference page. |
4303 | */ |
4304 | void DitaXmlGenerator::generateDetailedQmlMember(const Node* node, |
4305 | const InnerNode* relative, |
4306 | CodeMarker* marker) |
4307 | { |
4308 | QString marked; |
4309 | const QmlPropertyNode* qpn = 0; |
4310 | if (node->subType() == Node::QmlPropertyGroup) { |
4311 | const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node); |
4312 | NodeList::ConstIterator p = qpgn->childNodes().begin(); |
4313 | writeStartTag(DT_ul); |
4314 | while (p != qpgn->childNodes().end()) { |
4315 | if ((*p)->type() == Node::QmlProperty) { |
4316 | qpn = static_cast<const QmlPropertyNode*>(*p); |
4317 | writeStartTag(DT_li); |
4318 | writeGuidAttribute((Node*)qpn); |
4319 | QString attr; |
4320 | const ClassNode* cn = qpn->declarativeCppNode(); |
4321 | if (cn && !qpn->isWritable(myTree)) |
4322 | attr = "read-only"; |
4323 | if (qpgn->isDefault()) { |
4324 | if (!attr.isEmpty()) |
4325 | attr += " "; |
4326 | attr += "default"; |
4327 | } |
4328 | if (!attr.isEmpty()) |
4329 | xmlWriter().writeAttribute("outputclass",attr); |
4330 | generateQmlItem(qpn, relative, marker, false); |
4331 | writeEndTag(); // </li> |
4332 | } |
4333 | ++p; |
4334 | } |
4335 | writeEndTag(); // </ul> |
4336 | } |
4337 | else if (node->type() == Node::QmlSignal) { |
4338 | Node* n = const_cast<Node*>(node); |
4339 | writeStartTag(DT_ul); |
4340 | writeStartTag(DT_li); |
4341 | writeGuidAttribute(n); |
4342 | marked = getMarkedUpSynopsis(n, relative, marker, CodeMarker::Detailed); |
4343 | writeText(marked, marker, relative); |
4344 | writeEndTag(); // </li> |
4345 | writeEndTag(); // </ul> |
4346 | } |
4347 | else if (node->type() == Node::QmlMethod) { |
4348 | Node* n = const_cast<Node*>(node); |
4349 | writeStartTag(DT_ul); |
4350 | writeStartTag(DT_li); |
4351 | writeGuidAttribute(n); |
4352 | marked = getMarkedUpSynopsis(n, relative, marker, CodeMarker::Detailed); |
4353 | writeText(marked, marker, relative); |
4354 | writeEndTag(); // </li> |
4355 | writeEndTag(); // </ul> |
4356 | } |
4357 | generateStatus(node, marker); |
4358 | generateBody(node, marker); |
4359 | generateThreadSafeness(node, marker); |
4360 | generateSince(node, marker); |
4361 | generateAlsoList(node, marker); |
4362 | } |
4363 | |
4364 | /*! |
4365 | Output the "Inherits" line for the QML element, |
4366 | if there should be one. |
4367 | */ |
4368 | void DitaXmlGenerator::generateQmlInherits(const QmlClassNode* cn, |
4369 | CodeMarker* marker) |
4370 | { |
4371 | if (cn && !cn->links().empty()) { |
4372 | if (cn->links().contains(Node::InheritsLink)) { |
4373 | QPair<QString,QString> linkPair; |
4374 | linkPair = cn->links()[Node::InheritsLink]; |
4375 | QStringList strList(linkPair.first); |
4376 | const Node* n = myTree->findNode(strList,Node::Fake); |
4377 | if (n && n->subType() == Node::QmlClass) { |
4378 | const QmlClassNode* qcn = static_cast<const QmlClassNode*>(n); |
4379 | writeStartTag(DT_p); |
4380 | xmlWriter().writeAttribute("outputclass", "inherits"); |
4381 | Text text; |
4382 | text << "[Inherits "; |
4383 | text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn)); |
4384 | text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); |
4385 | text << Atom(Atom::String, linkPair.second); |
4386 | text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
4387 | text << "]"; |
4388 | generateText(text, cn, marker); |
4389 | writeEndTag(); // </p> |
4390 | } |
4391 | } |
4392 | } |
4393 | } |
4394 | |
4395 | /*! |
4396 | Output the "Inherit by" list for the QML element, |
4397 | if it is inherited by any other elements. |
4398 | */ |
4399 | void DitaXmlGenerator::generateQmlInheritedBy(const QmlClassNode* cn, |
4400 | CodeMarker* marker) |
4401 | { |
4402 | if (cn) { |
4403 | NodeList subs; |
4404 | QmlClassNode::subclasses(cn->name(),subs); |
4405 | if (!subs.isEmpty()) { |
4406 | Text text; |
4407 | text << Atom::ParaLeft << "Inherited by "; |
4408 | appendSortedQmlNames(text,cn,subs,marker); |
4409 | text << Atom::ParaRight; |
4410 | generateText(text, cn, marker); |
4411 | } |
4412 | } |
4413 | } |
4414 | |
4415 | /*! |
4416 | Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]" |
4417 | line for the QML element, if there should be one. |
4418 | |
4419 | If there is no class node, or if the class node status |
4420 | is set to Node::Internal, do nothing. |
4421 | */ |
4422 | void DitaXmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn, |
4423 | CodeMarker* marker) |
4424 | { |
4425 | const ClassNode* cn = qcn->classNode(); |
4426 | if (cn && (cn->status() != Node::Internal)) { |
4427 | writeStartTag(DT_p); |
4428 | xmlWriter().writeAttribute("outputclass", "instantiates"); |
4429 | Text text; |
4430 | text << "["; |
4431 | text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn)); |
4432 | text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); |
4433 | text << Atom(Atom::String, qcn->name()); |
4434 | text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
4435 | text << " instantiates the C++ class "; |
4436 | text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn)); |
4437 | text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); |
4438 | text << Atom(Atom::String, cn->name()); |
4439 | text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
4440 | text << "]"; |
4441 | generateText(text, qcn, marker); |
4442 | writeEndTag(); // </p> |
4443 | } |
4444 | } |
4445 | |
4446 | /*! |
4447 | Output the "[QmlGraphicsXxx is instantiated by QML element Xxx]" |
4448 | line for the class, if there should be one. |
4449 | |
4450 | If there is no QML element, or if the class node status |
4451 | is set to Node::Internal, do nothing. |
4452 | */ |
4453 | void DitaXmlGenerator::generateInstantiatedBy(const ClassNode* cn, |
4454 | CodeMarker* marker) |
4455 | { |
4456 | if (cn && cn->status() != Node::Internal && !cn->qmlElement().isEmpty()) { |
4457 | const Node* n = myTree->root()->findNode(cn->qmlElement(),Node::Fake); |
4458 | if (n && n->subType() == Node::QmlClass) { |
4459 | writeStartTag(DT_p); |
4460 | xmlWriter().writeAttribute("outputclass", "instantiated-by"); |
4461 | Text text; |
4462 | text << "["; |
4463 | text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn)); |
4464 | text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); |
4465 | text << Atom(Atom::String, cn->name()); |
4466 | text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
4467 | text << " is instantiated by QML element "; |
4468 | text << Atom(Atom::LinkNode,CodeMarker::stringForNode(n)); |
4469 | text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); |
4470 | text << Atom(Atom::String, n->name()); |
4471 | text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
4472 | text << "]"; |
4473 | generateText(text, cn, marker); |
4474 | writeEndTag(); // </p> |
4475 | } |
4476 | } |
4477 | } |
4478 | |
4479 | /*! |
4480 | Return the full qualification of the node \a n, but without |
4481 | the name of \a n itself. e.g. A::B::C |
4482 | */ |
4483 | QString DitaXmlGenerator::fullQualification(const Node* n) |
4484 | { |
4485 | QString fq; |
4486 | InnerNode* in = n->parent(); |
4487 | while (in) { |
4488 | if ((in->type() == Node::Class) || |
4489 | (in->type() == Node::Namespace)) { |
4490 | if (in->name().isEmpty()) |
4491 | break; |
4492 | if (fq.isEmpty()) |
4493 | fq = in->name(); |
4494 | else |
4495 | fq = in->name() + "::"+ fq; |
4496 | } |
4497 | else |
4498 | break; |
4499 | in = in->parent(); |
4500 | } |
4501 | return fq; |
4502 | } |
4503 | |
4504 | /*! |
4505 | Outputs the <cxxClassDerivations> element. |
4506 | \code |
4507 | <cxxClassDerivations> |
4508 | <cxxClassDerivation> |
4509 | ... |
4510 | </cxxClassDerivation> |
4511 | ... |
4512 | </cxxClassDerivations> |
4513 | \endcode |
4514 | |
4515 | The <cxxClassDerivation> element is: |
4516 | |
4517 | \code |
4518 | <cxxClassDerivation> |
4519 | <cxxClassDerivationAccessSpecifier value="public"/> |
4520 | <cxxClassBaseClass href="class_base">Base</cxxClassBaseClass> |
4521 | </cxxClassDerivation> |
4522 | \endcode |
4523 | */ |
4524 | void DitaXmlGenerator::writeDerivations(const ClassNode* cn, CodeMarker* marker) |
4525 | { |
4526 | QList<RelatedClass>::ConstIterator r; |
4527 | |
4528 | if (!cn->baseClasses().isEmpty()) { |
4529 | writeStartTag(DT_cxxClassDerivations); |
4530 | r = cn->baseClasses().begin(); |
4531 | while (r != cn->baseClasses().end()) { |
4532 | writeStartTag(DT_cxxClassDerivation); |
4533 | writeStartTag(DT_cxxClassDerivationAccessSpecifier); |
4534 | xmlWriter().writeAttribute("value",(*r).accessString()); |
4535 | writeEndTag(); // </cxxClassDerivationAccessSpecifier> |
4536 | |
4537 | // not included: <cxxClassDerivationVirtual> |
4538 | |
4539 | writeStartTag(DT_cxxClassBaseClass); |
4540 | QString attr = fileName((*r).node) + "#"+ (*r).node->guid(); |
4541 | xmlWriter().writeAttribute("href",attr); |
4542 | writeCharacters(marker->plainFullName((*r).node)); |
4543 | writeEndTag(); // </cxxClassBaseClass> |
4544 | |
4545 | // not included: <ClassBaseStruct> or <cxxClassBaseUnion> |
4546 | |
4547 | writeEndTag(); // </cxxClassDerivation> |
4548 | |
4549 | // not included: <cxxStructDerivation> |
4550 | |
4551 | ++r; |
4552 | } |
4553 | writeEndTag(); // </cxxClassDerivations> |
4554 | } |
4555 | } |
4556 | |
4557 | /*! |
4558 | Writes a <cxxXXXAPIItemLocation> element, depending on the |
4559 | type of the node \a n, which can be a class, function, enum, |
4560 | typedef, or property. |
4561 | */ |
4562 | void DitaXmlGenerator::writeLocation(const Node* n) |
4563 | { |
4564 | DitaTag s1, s2, s3, s4, s5, s6; |
4565 | s1 = DT_cxxClassAPIItemLocation; |
4566 | s2 = DT_cxxClassDeclarationFile; |
4567 | s3 = DT_cxxClassDeclarationFileLine; |
4568 | s4 = DT_LAST; |
4569 | if (n->type() == Node::Class || n->type() == Node::Namespace) { |
4570 | s1 = DT_cxxClassAPIItemLocation; |
4571 | s2 = DT_cxxClassDeclarationFile; |
4572 | s3 = DT_cxxClassDeclarationFileLine; |
4573 | } |
4574 | else if (n->type() == Node::Function) { |
4575 | FunctionNode* fn = const_cast<FunctionNode*>(static_cast<const FunctionNode*>(n)); |
4576 | if (fn->isMacro()) { |
4577 | s1 = DT_cxxDefineAPIItemLocation; |
4578 | s2 = DT_cxxDefineDeclarationFile; |
4579 | s3 = DT_cxxDefineDeclarationFileLine; |
4580 | } |
4581 | else { |
4582 | s1 = DT_cxxFunctionAPIItemLocation; |
4583 | s2 = DT_cxxFunctionDeclarationFile; |
4584 | s3 = DT_cxxFunctionDeclarationFileLine; |
4585 | } |
4586 | } |
4587 | else if (n->type() == Node::Enum) { |
4588 | s1 = DT_cxxEnumerationAPIItemLocation; |
4589 | s2 = DT_cxxEnumerationDeclarationFile; |
4590 | s3 = DT_cxxEnumerationDeclarationFileLine; |
4591 | s4 = DT_cxxEnumerationDefinitionFile; |
4592 | s5 = DT_cxxEnumerationDefinitionFileLineStart; |
4593 | s6 = DT_cxxEnumerationDefinitionFileLineEnd; |
4594 | } |
4595 | else if (n->type() == Node::Typedef) { |
4596 | s1 = DT_cxxTypedefAPIItemLocation; |
4597 | s2 = DT_cxxTypedefDeclarationFile; |
4598 | s3 = DT_cxxTypedefDeclarationFileLine; |
4599 | } |
4600 | else if ((n->type() == Node::Property) || |
4601 | (n->type() == Node::Variable)) { |
4602 | s1 = DT_cxxVariableAPIItemLocation; |
4603 | s2 = DT_cxxVariableDeclarationFile; |
4604 | s3 = DT_cxxVariableDeclarationFileLine; |
4605 | } |
4606 | writeStartTag(s1); |
4607 | writeStartTag(s2); |
4608 | xmlWriter().writeAttribute("name", "filePath"); |
4609 | xmlWriter().writeAttribute("value",n->location().filePath()); |
4610 | writeEndTag(); // </cxx<s2>DeclarationFile> |
4611 | writeStartTag(s3); |
4612 | xmlWriter().writeAttribute("name", "lineNumber"); |
4613 | QString lineNr; |
4614 | xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo())); |
4615 | writeEndTag(); // </cxx<s3>DeclarationFileLine> |
4616 | if (s4 != DT_LAST) { // zzz This stuff is temporary, I think. |
4617 | writeStartTag(s4); |
4618 | xmlWriter().writeAttribute("name", "filePath"); |
4619 | xmlWriter().writeAttribute("value",n->location().filePath()); |
4620 | writeEndTag(); // </cxx<s4>DefinitionFile> |
4621 | writeStartTag(s5); |
4622 | xmlWriter().writeAttribute("name", "lineNumber"); |
4623 | xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo())); |
4624 | writeEndTag(); // </cxx<s5>DefinitionFileLineStart> |
4625 | writeStartTag(s6); |
4626 | xmlWriter().writeAttribute("name", "lineNumber"); |
4627 | xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo())); |
4628 | writeEndTag(); // </cxx<s6>DefinitionFileLineEnd> |
4629 | } |
4630 | |
4631 | // not included: <cxxXXXDefinitionFile>, <cxxXXXDefinitionFileLineStart>, |
4632 | // and <cxxXXXDefinitionFileLineEnd> |
4633 | |
4634 | writeEndTag(); // </cxx<s1>ApiItemLocation> |
4635 | } |
4636 | |
4637 | /*! |
4638 | Write the <cxxFunction> elements. |
4639 | */ |
4640 | void DitaXmlGenerator::writeFunctions(const Section& s, |
4641 | const InnerNode* parent, |
4642 | CodeMarker* marker, |
4643 | const QString& attribute) |
4644 | { |
4645 | NodeList::ConstIterator m = s.members.begin(); |
4646 | while (m != s.members.end()) { |
4647 | if ((*m)->type() == Node::Function) { |
4648 | FunctionNode* fn = const_cast<FunctionNode*>(static_cast<const FunctionNode*>(*m)); |
4649 | writeStartTag(DT_cxxFunction); |
4650 | xmlWriter().writeAttribute("id",fn->guid()); |
4651 | if (!attribute.isEmpty()) |
4652 | xmlWriter().writeAttribute("outputclass",attribute); |
4653 | writeStartTag(DT_apiName); |
4654 | if (fn->metaness() == FunctionNode::Signal) |
4655 | xmlWriter().writeAttribute("class", "signal"); |
4656 | else if (fn->metaness() == FunctionNode::Slot) |
4657 | xmlWriter().writeAttribute("class", "slot"); |
4658 | writeCharacters(fn->name()); |
4659 | writeEndTag(); // </apiName> |
4660 | generateBrief(fn,marker); |
4661 | |
4662 | // not included: <prolog> |
4663 | |
4664 | writeStartTag(DT_cxxFunctionDetail); |
4665 | writeStartTag(DT_cxxFunctionDefinition); |
4666 | writeStartTag(DT_cxxFunctionAccessSpecifier); |
4667 | xmlWriter().writeAttribute("value",fn->accessString()); |
4668 | writeEndTag(); // <cxxFunctionAccessSpecifier> |
4669 | |
4670 | // not included: <cxxFunctionStorageClassSpecifierExtern> |
4671 | |
4672 | if (fn->isStatic()) { |
4673 | writeStartTag(DT_cxxFunctionStorageClassSpecifierStatic); |
4674 | xmlWriter().writeAttribute("name", "static"); |
4675 | xmlWriter().writeAttribute("value", "static"); |
4676 | writeEndTag(); // <cxxFunctionStorageClassSpecifierStatic> |
4677 | } |
4678 | |
4679 | // not included: <cxxFunctionStorageClassSpecifierMutable>, |
4680 | |
4681 | if (fn->isConst()) { |
4682 | writeStartTag(DT_cxxFunctionConst); |
4683 | xmlWriter().writeAttribute("name", "const"); |
4684 | xmlWriter().writeAttribute("value", "const"); |
4685 | writeEndTag(); // <cxxFunctionConst> |
4686 | } |
4687 | |
4688 | // not included: <cxxFunctionExplicit> |
4689 | // <cxxFunctionInline |
4690 | |
4691 | if (fn->virtualness() != FunctionNode::NonVirtual) { |
4692 | writeStartTag(DT_cxxFunctionVirtual); |
4693 | xmlWriter().writeAttribute("name", "virtual"); |
4694 | xmlWriter().writeAttribute("value", "virtual"); |
4695 | writeEndTag(); // <cxxFunctionVirtual> |
4696 | if (fn->virtualness() == FunctionNode::PureVirtual) { |
4697 | writeStartTag(DT_cxxFunctionPureVirtual); |
4698 | xmlWriter().writeAttribute("name", "pure virtual"); |
4699 | xmlWriter().writeAttribute("value", "pure virtual"); |
4700 | writeEndTag(); // <cxxFunctionPureVirtual> |
4701 | } |
4702 | } |
4703 | |
4704 | if (fn->name() == parent->name()) { |
4705 | writeStartTag(DT_cxxFunctionConstructor); |
4706 | xmlWriter().writeAttribute("name", "constructor"); |
4707 | xmlWriter().writeAttribute("value", "constructor"); |
4708 | writeEndTag(); // <cxxFunctionConstructor> |
4709 | } |
4710 | else if (fn->name()[0] == QChar( |
4711 | writeStartTag(DT_cxxFunctionDestructor); |
4712 | xmlWriter().writeAttribute("name", "destructor"); |
4713 | xmlWriter().writeAttribute("value", "destructor"); |
4714 | writeEndTag(); // <cxxFunctionDestructor> |
4715 | } |
4716 | else { |
4717 | writeStartTag(DT_cxxFunctionDeclaredType); |
4718 | QString src = marker->typified(fn->returnType()); |
4719 | replaceTypesWithLinks(fn,parent,marker,src); |
4720 | writeEndTag(); // <cxxFunctionDeclaredType> |
4721 | } |
4722 | |
4723 | // not included: <cxxFunctionReturnType> |
4724 | |
4725 | QString fq = fullQualification(fn); |
4726 | if (!fq.isEmpty()) { |
4727 | writeStartTag(DT_cxxFunctionScopedName); |
4728 | writeCharacters(fq); |
4729 | writeEndTag(); // <cxxFunctionScopedName> |
4730 | } |
4731 | writeStartTag(DT_cxxFunctionPrototype); |
4732 | writeCharacters(fn->signature(true)); |
4733 | writeEndTag(); // <cxxFunctionPrototype> |
4734 | |
4735 | QString fnl = fn->signature(false); |
4736 | int idx = fnl.indexOf( |
4737 | if (idx < 0) |
4738 | idx = 0; |
4739 | else |
4740 | ++idx; |
4741 | fnl = fn->parent()->name() + "::"+ fnl.mid(idx); |
4742 | writeStartTag(DT_cxxFunctionNameLookup); |
4743 | writeCharacters(fnl); |
4744 | writeEndTag(); // <cxxFunctionNameLookup> |
4745 | |
4746 | if (!fn->isInternal() && fn->isReimp() && fn->reimplementedFrom() != 0) { |
4747 | FunctionNode* rfn = (FunctionNode*)fn->reimplementedFrom(); |
4748 | if (rfn && !rfn->isInternal()) { |
4749 | writeStartTag(DT_cxxFunctionReimplemented); |
4750 | xmlWriter().writeAttribute("href",rfn->ditaXmlHref()); |
4751 | writeCharacters(marker->plainFullName(rfn)); |
4752 | writeEndTag(); // </cxxFunctionReimplemented> |
4753 | } |
4754 | } |
4755 | writeParameters(fn,parent,marker); |
4756 | writeLocation(fn); |
4757 | writeEndTag(); // <cxxFunctionDefinition> |
4758 | |
4759 | writeApiDesc(fn, marker, QString()); |
4760 | // generateAlsoList(inner, marker); |
4761 | |
4762 | // not included: <example> or <apiImpl> |
4763 | |
4764 | writeEndTag(); // </cxxFunctionDetail> |
4765 | writeEndTag(); // </cxxFunction> |
4766 | |
4767 | if (fn->metaness() == FunctionNode::Ctor || |
4768 | fn->metaness() == FunctionNode::Dtor || |
4769 | fn->overloadNumber() != 1) { |
4770 | } |
4771 | } |
4772 | ++m; |
4773 | } |
4774 | } |
4775 | |
4776 | static const QString typeTag("type"); |
4777 | static const QChar charLangle = |
4778 | static const QChar charAt = |
4779 | |
4780 | /*! |
4781 | This function replaces class and enum names with <apiRelation> |
4782 | elements, i.e. links. |
4783 | */ |
4784 | void DitaXmlGenerator::replaceTypesWithLinks(const Node* n, |
4785 | const InnerNode* parent, |
4786 | CodeMarker* marker, |
4787 | QString& src) |
4788 | { |
4789 | QStringRef arg; |
4790 | QStringRef par1; |
4791 | int srcSize = src.size(); |
4792 | QString text; |
4793 | for (int i=0; i<srcSize;) { |
4794 | if (src.at(i) == charLangle && src.at(i+1) == charAt) { |
4795 | if (!text.isEmpty()) { |
4796 | writeCharacters(text); |
4797 | text.clear(); |
4798 | } |
4799 | i += 2; |
4800 | if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) { |
4801 | const Node* tn = marker->resolveTarget(arg.toString(), myTree, parent, n); |
4802 | addLink(linkForNode(tn,parent),arg,DT_apiRelation); |
4803 | } |
4804 | } |
4805 | else { |
4806 | text += src.at(i++); |
4807 | } |
4808 | } |
4809 | if (!text.isEmpty()) { |
4810 | writeCharacters(text); |
4811 | text.clear(); |
4812 | } |
4813 | } |
4814 | |
4815 | /*! |
4816 | This function writes the <cxxFunctionParameters> element. |
4817 | */ |
4818 | void DitaXmlGenerator::writeParameters(const FunctionNode* fn, |
4819 | const InnerNode* parent, |
4820 | CodeMarker* marker) |
4821 | { |
4822 | const QList<Parameter>& parameters = fn->parameters(); |
4823 | if (!parameters.isEmpty()) { |
4824 | writeStartTag(DT_cxxFunctionParameters); |
4825 | QList<Parameter>::ConstIterator p = parameters.begin(); |
4826 | while (p != parameters.end()) { |
4827 | writeStartTag(DT_cxxFunctionParameter); |
4828 | writeStartTag(DT_cxxFunctionParameterDeclaredType); |
4829 | QString src = marker->typified((*p).leftType()); |
4830 | replaceTypesWithLinks(fn,parent,marker,src); |
4831 | //writeCharacters((*p).leftType()); |
4832 | if (!(*p).rightType().isEmpty()) |
4833 | writeCharacters((*p).rightType()); |
4834 | writeEndTag(); // <cxxFunctionParameterDeclaredType> |
4835 | writeStartTag(DT_cxxFunctionParameterDeclarationName); |
4836 | writeCharacters((*p).name()); |
4837 | writeEndTag(); // <cxxFunctionParameterDeclarationName> |
4838 | |
4839 | // not included: <cxxFunctionParameterDefinitionName> |
4840 | |
4841 | if (!(*p).defaultValue().isEmpty()) { |
4842 | writeStartTag(DT_cxxFunctionParameterDefaultValue); |
4843 | writeCharacters((*p).defaultValue()); |
4844 | writeEndTag(); // <cxxFunctionParameterDefaultValue> |
4845 | } |
4846 | |
4847 | // not included: <apiDefNote> |
4848 | |
4849 | writeEndTag(); // <cxxFunctionParameter> |
4850 | ++p; |
4851 | } |
4852 | writeEndTag(); // <cxxFunctionParameters> |
4853 | } |
4854 | } |
4855 | |
4856 | /*! |
4857 | This function writes the enum types. |
4858 | */ |
4859 | void DitaXmlGenerator::writeEnumerations(const Section& s, |
4860 | CodeMarker* marker, |
4861 | const QString& attribute) |
4862 | { |
4863 | NodeList::ConstIterator m = s.members.begin(); |
4864 | while (m != s.members.end()) { |
4865 | if ((*m)->type() == Node::Enum) { |
4866 | const EnumNode* en = static_cast<const EnumNode*>(*m); |
4867 | writeStartTag(DT_cxxEnumeration); |
4868 | xmlWriter().writeAttribute("id",en->guid()); |
4869 | if (!attribute.isEmpty()) |
4870 | xmlWriter().writeAttribute("outputclass",attribute); |
4871 | writeStartTag(DT_apiName); |
4872 | writeCharacters(en->name()); |
4873 | writeEndTag(); // </apiName> |
4874 | generateBrief(en,marker); |
4875 | |
4876 | // not included <prolog> |
4877 | |
4878 | writeStartTag(DT_cxxEnumerationDetail); |
4879 | writeStartTag(DT_cxxEnumerationDefinition); |
4880 | writeStartTag(DT_cxxEnumerationAccessSpecifier); |
4881 | xmlWriter().writeAttribute("value",en->accessString()); |
4882 | writeEndTag(); // <cxxEnumerationAccessSpecifier> |
4883 | |
4884 | QString fq = fullQualification(en); |
4885 | if (!fq.isEmpty()) { |
4886 | writeStartTag(DT_cxxEnumerationScopedName); |
4887 | writeCharacters(fq); |
4888 | writeEndTag(); // <cxxEnumerationScopedName> |
4889 | } |
4890 | const QList<EnumItem>& items = en->items(); |
4891 | if (!items.isEmpty()) { |
4892 | writeStartTag(DT_cxxEnumerationPrototype); |
4893 | writeCharacters(en->name()); |
4894 | xmlWriter().writeCharacters(" = { "); |
4895 | QList<EnumItem>::ConstIterator i = items.begin(); |
4896 | while (i != items.end()) { |
4897 | writeCharacters((*i).name()); |
4898 | if (!(*i).value().isEmpty()) { |
4899 | xmlWriter().writeCharacters(" = "); |
4900 | writeCharacters((*i).value()); |
4901 | } |
4902 | ++i; |
4903 | if (i != items.end()) |
4904 | xmlWriter().writeCharacters(", "); |
4905 | } |
4906 | xmlWriter().writeCharacters(" }"); |
4907 | writeEndTag(); // <cxxEnumerationPrototype> |
4908 | } |
4909 | |
4910 | writeStartTag(DT_cxxEnumerationNameLookup); |
4911 | writeCharacters(en->parent()->name() + "::"+ en->name()); |
4912 | writeEndTag(); // <cxxEnumerationNameLookup> |
4913 | |
4914 | // not included: <cxxEnumerationReimplemented> |
4915 | |
4916 | if (!items.isEmpty()) { |
4917 | writeStartTag(DT_cxxEnumerators); |
4918 | QList<EnumItem>::ConstIterator i = items.begin(); |
4919 | while (i != items.end()) { |
4920 | writeStartTag(DT_cxxEnumerator); |
4921 | writeStartTag(DT_apiName); |
4922 | writeCharacters((*i).name()); |
4923 | writeEndTag(); // </apiName> |
4924 | |
4925 | QString fq = fullQualification(en->parent()); |
4926 | if (!fq.isEmpty()) { |
4927 | writeStartTag(DT_cxxEnumeratorScopedName); |
4928 | writeCharacters(fq + "::"+ (*i).name()); |
4929 | writeEndTag(); // <cxxEnumeratorScopedName> |
4930 | } |
4931 | writeStartTag(DT_cxxEnumeratorPrototype); |
4932 | writeCharacters((*i).name()); |
4933 | writeEndTag(); // <cxxEnumeratorPrototype> |
4934 | writeStartTag(DT_cxxEnumeratorNameLookup); |
4935 | writeCharacters(en->parent()->name() + "::"+ (*i).name()); |
4936 | writeEndTag(); // <cxxEnumeratorNameLookup> |
4937 | |
4938 | if (!(*i).value().isEmpty()) { |
4939 | writeStartTag(DT_cxxEnumeratorInitialiser); |
4940 | xmlWriter().writeAttribute("value", (*i).value()); |
4941 | writeEndTag(); // <cxxEnumeratorInitialiser> |
4942 | } |
4943 | |
4944 | // not included: <cxxEnumeratorAPIItemLocation> |
4945 | |
4946 | if (!(*i).text().isEmpty()) { |
4947 | writeStartTag(DT_apiDesc); |
4948 | generateText((*i).text(), en, marker); |
4949 | writeEndTag(); // </apiDesc> |
4950 | } |
4951 | writeEndTag(); // <cxxEnumerator> |
4952 | ++i; |
4953 | } |
4954 | writeEndTag(); // <cxxEnumerators> |
4955 | } |
4956 | |
4957 | writeLocation(en); |
4958 | writeEndTag(); // <cxxEnumerationDefinition> |
4959 | |
4960 | writeApiDesc(en, marker, QString()); |
4961 | |
4962 | // not included: <example> or <apiImpl> |
4963 | |
4964 | writeEndTag(); // </cxxEnumerationDetail> |
4965 | |
4966 | // not included: <related-links> |
4967 | |
4968 | writeEndTag(); // </cxxEnumeration> |
4969 | } |
4970 | ++m; |
4971 | } |
4972 | } |
4973 | |
4974 | /*! |
4975 | This function writes the output for the \typedef commands. |
4976 | */ |
4977 | void DitaXmlGenerator::writeTypedefs(const Section& s, |
4978 | CodeMarker* marker, |
4979 | const QString& attribute) |
4980 | |
4981 | { |
4982 | NodeList::ConstIterator m = s.members.begin(); |
4983 | while (m != s.members.end()) { |
4984 | if ((*m)->type() == Node::Typedef) { |
4985 | const TypedefNode* tn = static_cast<const TypedefNode*>(*m); |
4986 | writeStartTag(DT_cxxTypedef); |
4987 | xmlWriter().writeAttribute("id",tn->guid()); |
4988 | if (!attribute.isEmpty()) |
4989 | xmlWriter().writeAttribute("outputclass",attribute); |
4990 | writeStartTag(DT_apiName); |
4991 | writeCharacters(tn->name()); |
4992 | writeEndTag(); // </apiName> |
4993 | generateBrief(tn,marker); |
4994 | |
4995 | // not included: <prolog> |
4996 | |
4997 | writeStartTag(DT_cxxTypedefDetail); |
4998 | writeStartTag(DT_cxxTypedefDefinition); |
4999 | writeStartTag(DT_cxxTypedefAccessSpecifier); |
5000 | xmlWriter().writeAttribute("value",tn->accessString()); |
5001 | writeEndTag(); // <cxxTypedefAccessSpecifier> |
5002 | |
5003 | // not included: <cxxTypedefDeclaredType> |
5004 | |
5005 | QString fq = fullQualification(tn); |
5006 | if (!fq.isEmpty()) { |
5007 | writeStartTag(DT_cxxTypedefScopedName); |
5008 | writeCharacters(fq); |
5009 | writeEndTag(); // <cxxTypedefScopedName> |
5010 | } |
5011 | |
5012 | // not included: <cxxTypedefPrototype> |
5013 | |
5014 | writeStartTag(DT_cxxTypedefNameLookup); |
5015 | writeCharacters(tn->parent()->name() + "::"+ tn->name()); |
5016 | writeEndTag(); // <cxxTypedefNameLookup> |
5017 | |
5018 | // not included: <cxxTypedefReimplemented> |
5019 | |
5020 | writeLocation(tn); |
5021 | writeEndTag(); // <cxxTypedefDefinition> |
5022 | |
5023 | writeApiDesc(tn, marker, QString()); |
5024 | |
5025 | // not included: <example> or <apiImpl> |
5026 | |
5027 | writeEndTag(); // </cxxTypedefDetail> |
5028 | |
5029 | // not included: <related-links> |
5030 | |
5031 | writeEndTag(); // </cxxTypedef> |
5032 | } |
5033 | ++m; |
5034 | } |
5035 | } |
5036 | |
5037 | /*! |
5038 | This function writes the output for the \property commands. |
5039 | This is the Q_PROPERTYs. |
5040 | */ |
5041 | void DitaXmlGenerator::writeProperties(const Section& s, |
5042 | CodeMarker* marker, |
5043 | const QString& attribute) |
5044 | { |
5045 | NodeList::ConstIterator m = s.members.begin(); |
5046 | while (m != s.members.end()) { |
5047 | if ((*m)->type() == Node::Property) { |
5048 | const PropertyNode* pn = static_cast<const PropertyNode*>(*m); |
5049 | writeStartTag(DT_cxxVariable); |
5050 | xmlWriter().writeAttribute("id",pn->guid()); |
5051 | if (!attribute.isEmpty()) |
5052 | xmlWriter().writeAttribute("outputclass",attribute); |
5053 | writeStartTag(DT_apiName); |
5054 | writeCharacters(pn->name()); |
5055 | writeEndTag(); // </apiName> |
5056 | generateBrief(pn,marker); |
5057 | |
5058 | // not included: <prolog> |
5059 | |
5060 | writeStartTag(DT_cxxVariableDetail); |
5061 | writeStartTag(DT_cxxVariableDefinition); |
5062 | writeStartTag(DT_cxxVariableAccessSpecifier); |
5063 | xmlWriter().writeAttribute("value",pn->accessString()); |
5064 | writeEndTag(); // <cxxVariableAccessSpecifier> |
5065 | |
5066 | // not included: <cxxVariableStorageClassSpecifierExtern>, |
5067 | // <cxxVariableStorageClassSpecifierStatic>, |
5068 | // <cxxVariableStorageClassSpecifierMutable>, |
5069 | // <cxxVariableConst>, <cxxVariableVolatile> |
5070 | |
5071 | if (!pn->qualifiedDataType().isEmpty()) { |
5072 | writeStartTag(DT_cxxVariableDeclaredType); |
5073 | writeCharacters(pn->qualifiedDataType()); |
5074 | writeEndTag(); // <cxxVariableDeclaredType> |
5075 | } |
5076 | QString fq = fullQualification(pn); |
5077 | if (!fq.isEmpty()) { |
5078 | writeStartTag(DT_cxxVariableScopedName); |
5079 | writeCharacters(fq); |
5080 | writeEndTag(); // <cxxVariableScopedName> |
5081 | } |
5082 | |
5083 | writeStartTag(DT_cxxVariablePrototype); |
5084 | xmlWriter().writeCharacters("Q_PROPERTY("); |
5085 | writeCharacters(pn->qualifiedDataType()); |
5086 | xmlWriter().writeCharacters(" "); |
5087 | writeCharacters(pn->name()); |
5088 | writePropertyParameter("READ",pn->getters()); |
5089 | writePropertyParameter("WRITE",pn->setters()); |
5090 | writePropertyParameter("RESET",pn->resetters()); |
5091 | writePropertyParameter("NOTIFY",pn->notifiers()); |
5092 | if (pn->isDesignable() != pn->designableDefault()) { |
5093 | xmlWriter().writeCharacters(" DESIGNABLE "); |
5094 | if (!pn->runtimeDesignabilityFunction().isEmpty()) |
5095 | writeCharacters(pn->runtimeDesignabilityFunction()); |
5096 | else |
5097 | xmlWriter().writeCharacters(pn->isDesignable() ? "true": "false"); |
5098 | } |
5099 | if (pn->isScriptable() != pn->scriptableDefault()) { |
5100 | xmlWriter().writeCharacters(" SCRIPTABLE "); |
5101 | if (!pn->runtimeScriptabilityFunction().isEmpty()) |
5102 | writeCharacters(pn->runtimeScriptabilityFunction()); |
5103 | else |
5104 | xmlWriter().writeCharacters(pn->isScriptable() ? "true": "false"); |
5105 | } |
5106 | if (pn->isWritable() != pn->writableDefault()) { |
5107 | xmlWriter().writeCharacters(" STORED "); |
5108 | xmlWriter().writeCharacters(pn->isStored() ? "true": "false"); |
5109 | } |
5110 | if (pn->isUser() != pn->userDefault()) { |
5111 | xmlWriter().writeCharacters(" USER "); |
5112 | xmlWriter().writeCharacters(pn->isUser() ? "true": "false"); |
5113 | } |
5114 | if (pn->isConstant()) |
5115 | xmlWriter().writeCharacters(" CONSTANT"); |
5116 | if (pn->isFinal()) |
5117 | xmlWriter().writeCharacters(" FINAL"); |
5118 | xmlWriter().writeCharacters(")"); |
5119 | writeEndTag(); // <cxxVariablePrototype> |
5120 | |
5121 | writeStartTag(DT_cxxVariableNameLookup); |
5122 | writeCharacters(pn->parent()->name() + "::"+ pn->name()); |
5123 | writeEndTag(); // <cxxVariableNameLookup> |
5124 | |
5125 | if (pn->overriddenFrom() != 0) { |
5126 | PropertyNode* opn = (PropertyNode*)pn->overriddenFrom(); |
5127 | writeStartTag(DT_cxxVariableReimplemented); |
5128 | xmlWriter().writeAttribute("href",opn->ditaXmlHref()); |
5129 | writeCharacters(marker->plainFullName(opn)); |
5130 | writeEndTag(); // </cxxVariableReimplemented> |
5131 | } |
5132 | |
5133 | writeLocation(pn); |
5134 | writeEndTag(); // <cxxVariableDefinition> |
5135 | |
5136 | writeApiDesc(pn, marker, QString()); |
5137 | |
5138 | // not included: <example> or <apiImpl> |
5139 | |
5140 | writeEndTag(); // </cxxVariableDetail> |
5141 | |
5142 | // not included: <related-links> |
5143 | |
5144 | writeEndTag(); // </cxxVariable> |
5145 | } |
5146 | ++m; |
5147 | } |
5148 | } |
5149 | |
5150 | /*! |
5151 | This function outputs the nodes resulting from \variable commands. |
5152 | */ |
5153 | void DitaXmlGenerator::writeDataMembers(const Section& s, |
5154 | CodeMarker* marker, |
5155 | const QString& attribute) |
5156 | { |
5157 | NodeList::ConstIterator m = s.members.begin(); |
5158 | while (m != s.members.end()) { |
5159 | if ((*m)->type() == Node::Variable) { |
5160 | const VariableNode* vn = static_cast<const VariableNode*>(*m); |
5161 | writeStartTag(DT_cxxVariable); |
5162 | xmlWriter().writeAttribute("id",vn->guid()); |
5163 | if (!attribute.isEmpty()) |
5164 | xmlWriter().writeAttribute("outputclass",attribute); |
5165 | writeStartTag(DT_apiName); |
5166 | writeCharacters(vn->name()); |
5167 | writeEndTag(); // </apiName> |
5168 | generateBrief(vn,marker); |
5169 | |
5170 | // not included: <prolog> |
5171 | |
5172 | writeStartTag(DT_cxxVariableDetail); |
5173 | writeStartTag(DT_cxxVariableDefinition); |
5174 | writeStartTag(DT_cxxVariableAccessSpecifier); |
5175 | xmlWriter().writeAttribute("value",vn->accessString()); |
5176 | writeEndTag(); // <cxxVariableAccessSpecifier> |
5177 | |
5178 | // not included: <cxxVAriableStorageClassSpecifierExtern> |
5179 | |
5180 | if (vn->isStatic()) { |
5181 | writeStartTag(DT_cxxVariableStorageClassSpecifierStatic); |
5182 | xmlWriter().writeAttribute("name", "static"); |
5183 | xmlWriter().writeAttribute("value", "static"); |
5184 | writeEndTag(); // <cxxVariableStorageClassSpecifierStatic> |
5185 | } |
5186 | |
5187 | // not included: <cxxVAriableStorageClassSpecifierMutable>, |
5188 | // <cxxVariableConst>, <cxxVariableVolatile> |
5189 | |
5190 | writeStartTag(DT_cxxVariableDeclaredType); |
5191 | writeCharacters(vn->leftType()); |
5192 | if (!vn->rightType().isEmpty()) |
5193 | writeCharacters(vn->rightType()); |
5194 | writeEndTag(); // <cxxVariableDeclaredType> |
5195 | |
5196 | QString fq = fullQualification(vn); |
5197 | if (!fq.isEmpty()) { |
5198 | writeStartTag(DT_cxxVariableScopedName); |
5199 | writeCharacters(fq); |
5200 | writeEndTag(); // <cxxVariableScopedName> |
5201 | } |
5202 | |
5203 | writeStartTag(DT_cxxVariablePrototype); |
5204 | writeCharacters(vn->leftType() + " "); |
5205 | //writeCharacters(vn->parent()->name() + "::" + vn->name()); |
5206 | writeCharacters(vn->name()); |
5207 | if (!vn->rightType().isEmpty()) |
5208 | writeCharacters(vn->rightType()); |
5209 | writeEndTag(); // <cxxVariablePrototype> |
5210 | |
5211 | writeStartTag(DT_cxxVariableNameLookup); |
5212 | writeCharacters(vn->parent()->name() + "::"+ vn->name()); |
5213 | writeEndTag(); // <cxxVariableNameLookup> |
5214 | |
5215 | // not included: <cxxVariableReimplemented> |
5216 | |
5217 | writeLocation(vn); |
5218 | writeEndTag(); // <cxxVariableDefinition> |
5219 | |
5220 | writeApiDesc(vn, marker, QString()); |
5221 | |
5222 | // not included: <example> or <apiImpl> |
5223 | |
5224 | writeEndTag(); // </cxxVariableDetail> |
5225 | |
5226 | // not included: <related-links> |
5227 | |
5228 | writeEndTag(); // </cxxVariable> |
5229 | } |
5230 | ++m; |
5231 | } |
5232 | } |
5233 | |
5234 | /*! |
5235 | This function writes a \macro as a <cxxDefine>. |
5236 | */ |
5237 | void DitaXmlGenerator::writeMacros(const Section& s, |
5238 | CodeMarker* marker, |
5239 | const QString& attribute) |
5240 | { |
5241 | NodeList::ConstIterator m = s.members.begin(); |
5242 | while (m != s.members.end()) { |
5243 | if ((*m)->type() == Node::Function) { |
5244 | const FunctionNode* fn = static_cast<const FunctionNode*>(*m); |
5245 | if (fn->isMacro()) { |
5246 | writeStartTag(DT_cxxDefine); |
5247 | xmlWriter().writeAttribute("id",fn->guid()); |
5248 | if (!attribute.isEmpty()) |
5249 | xmlWriter().writeAttribute("outputclass",attribute); |
5250 | writeStartTag(DT_apiName); |
5251 | writeCharacters(fn->name()); |
5252 | writeEndTag(); // </apiName> |
5253 | generateBrief(fn,marker); |
5254 | |
5255 | // not included: <prolog> |
5256 | |
5257 | writeStartTag(DT_cxxDefineDetail); |
5258 | writeStartTag(DT_cxxDefineDefinition); |
5259 | writeStartTag(DT_cxxDefineAccessSpecifier); |
5260 | xmlWriter().writeAttribute("value",fn->accessString()); |
5261 | writeEndTag(); // <cxxDefineAccessSpecifier> |
5262 | |
5263 | writeStartTag(DT_cxxDefinePrototype); |
5264 | xmlWriter().writeCharacters("#define "); |
5265 | writeCharacters(fn->name()); |
5266 | if (fn->metaness() == FunctionNode::MacroWithParams) { |
5267 | QStringList params = fn->parameterNames(); |
5268 | if (!params.isEmpty()) { |
5269 | xmlWriter().writeCharacters("("); |
5270 | for (int i = 0; i < params.size(); ++i) { |
5271 | if (params[i].isEmpty()) |
5272 | xmlWriter().writeCharacters("..."); |
5273 | else |
5274 | writeCharacters(params[i]); |
5275 | if ((i+1) < params.size()) |
5276 | xmlWriter().writeCharacters(", "); |
5277 | } |
5278 | xmlWriter().writeCharacters(")"); |
5279 | } |
5280 | } |
5281 | writeEndTag(); // <cxxDefinePrototype> |
5282 | |
5283 | writeStartTag(DT_cxxDefineNameLookup); |
5284 | writeCharacters(fn->name()); |
5285 | writeEndTag(); // <cxxDefineNameLookup> |
5286 | |
5287 | if (fn->reimplementedFrom() != 0) { |
5288 | FunctionNode* rfn = (FunctionNode*)fn->reimplementedFrom(); |
5289 | writeStartTag(DT_cxxDefineReimplemented); |
5290 | xmlWriter().writeAttribute("href",rfn->ditaXmlHref()); |
5291 | writeCharacters(marker->plainFullName(rfn)); |
5292 | writeEndTag(); // </cxxDefineReimplemented> |
5293 | } |
5294 | |
5295 | if (fn->metaness() == FunctionNode::MacroWithParams) { |
5296 | QStringList params = fn->parameterNames(); |
5297 | if (!params.isEmpty()) { |
5298 | writeStartTag(DT_cxxDefineParameters); |
5299 | for (int i = 0; i < params.size(); ++i) { |
5300 | writeStartTag(DT_cxxDefineParameter); |
5301 | writeStartTag(DT_cxxDefineParameterDeclarationName); |
5302 | writeCharacters(params[i]); |
5303 | writeEndTag(); // <cxxDefineParameterDeclarationName> |
5304 | |
5305 | // not included: <apiDefNote> |
5306 | |
5307 | writeEndTag(); // <cxxDefineParameter> |
5308 | } |
5309 | writeEndTag(); // <cxxDefineParameters> |
5310 | } |
5311 | } |
5312 | |
5313 | writeLocation(fn); |
5314 | writeEndTag(); // <cxxDefineDefinition> |
5315 | |
5316 | writeApiDesc(fn, marker, QString()); |
5317 | |
5318 | // not included: <example> or <apiImpl> |
5319 | |
5320 | writeEndTag(); // </cxxDefineDetail> |
5321 | |
5322 | // not included: <related-links> |
5323 | |
5324 | writeEndTag(); // </cxxDefine> |
5325 | } |
5326 | } |
5327 | ++m; |
5328 | } |
5329 | } |
5330 | |
5331 | /*! |
5332 | This function writes one parameter of a Q_PROPERTY macro. |
5333 | The property is identified by \a tag ("READ" "WRIE" etc), |
5334 | and it is found in the 'a nlist. |
5335 | */ |
5336 | void DitaXmlGenerator::writePropertyParameter(const QString& tag, const NodeList& nlist) |
5337 | { |
5338 | NodeList::const_iterator n = nlist.begin(); |
5339 | while (n != nlist.end()) { |
5340 | xmlWriter().writeCharacters(" "); |
5341 | writeCharacters(tag); |
5342 | xmlWriter().writeCharacters(" "); |
5343 | writeCharacters((*n)->name()); |
5344 | ++n; |
5345 | } |
5346 | } |
5347 | |
5348 | /*! |
5349 | Calls beginSubPage() in the base class to open the file. |
5350 | Then creates a new XML stream writer using the IO device |
5351 | from opened file and pushes the XML writer onto a stackj. |
5352 | Creates the file named \a fileName in the output directory. |
5353 | Attaches a QTextStream to the created file, which is written |
5354 | to all over the place using out(). Finally, it sets some |
5355 | parameters in the XML writer and calls writeStartDocument(). |
5356 | */ |
5357 | void DitaXmlGenerator::beginSubPage(const Location& location, |
5358 | const QString& fileName) |
5359 | { |
5360 | PageGenerator::beginSubPage(location,fileName); |
5361 | (void) lookupGuidMap(fileName); |
5362 | QXmlStreamWriter* writer = new QXmlStreamWriter(out().device()); |
5363 | xmlWriterStack.push(writer); |
5364 | writer->setAutoFormatting(true); |
5365 | writer->setAutoFormattingIndent(4); |
5366 | writer->writeStartDocument(); |
5367 | clearSectionNesting(); |
5368 | } |
5369 | |
5370 | /*! |
5371 | Calls writeEndDocument() and then pops the XML stream writer |
5372 | off the stack and deletes it. Then it calls endSubPage() in |
5373 | the base class to close the device. |
5374 | */ |
5375 | void DitaXmlGenerator::endSubPage() |
5376 | { |
5377 | if (inSection()) |
5378 | qDebug() << "Missing </section> in"<< outFileName() << sectionNestingLevel; |
5379 | xmlWriter().writeEndDocument(); |
5380 | delete xmlWriterStack.pop(); |
5381 | PageGenerator::endSubPage(); |
5382 | } |
5383 | |
5384 | /*! |
5385 | Returns a reference to the XML stream writer currently in use. |
5386 | There is one XML stream writer open for each XML file being |
5387 | written, and they are kept on a stack. The one on top of the |
5388 | stack is the one being written to at the moment. |
5389 | */ |
5390 | QXmlStreamWriter& DitaXmlGenerator::xmlWriter() |
5391 | { |
5392 | return *xmlWriterStack.top(); |
5393 | } |
5394 | |
5395 | /*! |
5396 | Writes the \e {<apiDesc>} element for \a node to the current XML |
5397 | stream using the code \a marker and the \a title. |
5398 | */ |
5399 | void DitaXmlGenerator::writeApiDesc(const Node* node, |
5400 | CodeMarker* marker, |
5401 | const QString& title) |
5402 | { |
5403 | if (!node->doc().isEmpty()) { |
5404 | inDetailedDescription = true; |
5405 | enterApiDesc(QString(),title); |
5406 | generateBody(node, marker); |
5407 | generateAlsoList(node, marker); |
5408 | leaveSection(); |
5409 | } |
5410 | inDetailedDescription = false; |
5411 | } |
5412 | |
5413 | /*! |
5414 | Write the nested class elements. |
5415 | */ |
5416 | void DitaXmlGenerator::writeNestedClasses(const Section& s, |
5417 | const Node* n) |
5418 | { |
5419 | if (s.members.isEmpty()) |
5420 | return; |
5421 | writeStartTag(DT_cxxClassNested); |
5422 | writeStartTag(DT_cxxClassNestedDetail); |
5423 | |
5424 | NodeList::ConstIterator m = s.members.begin(); |
5425 | while (m != s.members.end()) { |
5426 | if ((*m)->type() == Node::Class) { |
5427 | writeStartTag(DT_cxxClassNestedClass); |
5428 | QString link = linkForNode((*m), n); |
5429 | xmlWriter().writeAttribute("href", link); |
5430 | QString name = n->name() + "::"+ (*m)->name(); |
5431 | writeCharacters(name); |
5432 | writeEndTag(); // <cxxClassNestedClass> |
5433 | } |
5434 | ++m; |
5435 | } |
5436 | writeEndTag(); // <cxxClassNestedDetail> |
5437 | writeEndTag(); // <cxxClassNested> |
5438 | } |
5439 | |
5440 | /*! |
5441 | Recursive writing of DITA XML files from the root \a node. |
5442 | */ |
5443 | void |
5444 | DitaXmlGenerator::generateInnerNode(const InnerNode* node) |
5445 | { |
5446 | if (!node->url().isNull()) |
5447 | return; |
5448 | |
5449 | if (node->type() == Node::Fake) { |
5450 | const FakeNode *fakeNode = static_cast<const FakeNode *>(node); |
5451 | if (fakeNode->subType() == Node::ExternalPage) |
5452 | return; |
5453 | if (fakeNode->subType() == Node::Image) |
5454 | return; |
5455 | if (fakeNode->subType() == Node::QmlPropertyGroup) |
5456 | return; |
5457 | if (fakeNode->subType() == Node::Page) { |
5458 | if (node->count() > 0) |
5459 | qDebug("PAGE %s HAS CHILDREN", qPrintable(fakeNode->title())); |
5460 | } |
5461 | } |
5462 | |
5463 | /* |
5464 | Obtain a code marker for the source file. |
5465 | */ |
5466 | CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath()); |
5467 | |
5468 | if (node->parent() != 0) { |
5469 | beginSubPage(node->location(), fileName(node)); |
5470 | if (node->type() == Node::Namespace || node->type() == Node::Class) { |
5471 | generateClassLikeNode(node, marker); |
5472 | } |
5473 | else if (node->type() == Node::Fake) { |
5474 | if (node->subType() == Node::HeaderFile) |
5475 | generateClassLikeNode(node, marker); |
5476 | else if (node->subType() == Node::QmlClass) |
5477 | generateClassLikeNode(node, marker); |
5478 | else |
5479 | generateFakeNode(static_cast<const FakeNode*>(node), marker); |
5480 | } |
5481 | endSubPage(); |
5482 | } |
5483 | |
5484 | NodeList::ConstIterator c = node->childNodes().begin(); |
5485 | while (c != node->childNodes().end()) { |
5486 | if ((*c)->isInnerNode() && (*c)->access() != Node::Private) |
5487 | generateInnerNode((const InnerNode*) *c); |
5488 | ++c; |
5489 | } |
5490 | } |
5491 | |
5492 | /*! |
5493 | Returns true if \a format is "XML" or "HTML" . |
5494 | */ |
5495 | bool DitaXmlGenerator::canHandleFormat(const QString& format) |
5496 | { |
5497 | return (format == "HTML") || (format == this->format()); |
5498 | } |
5499 | |
5500 | void DitaXmlGenerator::writeDitaMap() |
5501 | { |
5502 | beginSubPage(Location(),"qt-dita-map.xml"); |
5503 | |
5504 | QString doctype; |
5505 | doctype = "<!DOCTYPE cxxAPIMap PUBLIC \"-//NOKIA//DTD DITA C++ API Map Reference Type v0.6.0//EN\" \"dtd/cxxAPIMap.dtd\">"; |
5506 | |
5507 | xmlWriter().writeDTD(doctype); |
5508 | writeStartTag(DT_cxxAPIMap); |
5509 | xmlWriter().writeAttribute("id", "Qt-DITA-Map"); |
5510 | xmlWriter().writeAttribute("title", "Qt DITA Map"); |
5511 | writeStartTag(DT_topicmeta); |
5512 | writeStartTag(DT_shortdesc); |
5513 | xmlWriter().writeCharacters("The top level map for the Qt documentation"); |
5514 | writeEndTag(); // </shortdesc> |
5515 | writeEndTag(); // </topicmeta> |
5516 | GuidMaps::iterator i = guidMaps.begin(); |
5517 | while (i != guidMaps.end()) { |
5518 | writeStartTag(DT_topicref); |
5519 | xmlWriter().writeAttribute("href",i.key()); |
5520 | xmlWriter().writeAttribute("type", "topic"); |
5521 | writeEndTag(); // </topicref> |
5522 | ++i; |
5523 | } |
5524 | endSubPage(); |
5525 | } |
5526 | |
5527 | /*! |
5528 | Looks up the tag name for \a t in the map of metadata |
5529 | values for the current topic in \a inner. If a value |
5530 | for the tag is found, the element is written with the |
5531 | found value. Otherwise if \a force is set, an empty |
5532 | element is written using the tag. |
5533 | |
5534 | Returns true or false depending on whether it writes |
5535 | an element using the tag \a t. |
5536 | |
5537 | \note If \a t is found in the metadata map, it is erased. |
5538 | i.e. Once you call this function for a particular \a t, |
5539 | you consume \a t. |
5540 | */ |
5541 | bool DitaXmlGenerator::writeMetadataElement(const InnerNode* inner, |
5542 | DitaXmlGenerator::DitaTag t, |
5543 | bool force) |
5544 | { |
5545 | QString s = getMetadataElement(inner,t); |
5546 | if (s.isEmpty() && !force) |
5547 | return false; |
5548 | writeStartTag(t); |
5549 | if (!s.isEmpty()) |
5550 | xmlWriter().writeCharacters(s); |
5551 | writeEndTag(); |
5552 | return true; |
5553 | } |
5554 | |
5555 | |
5556 | /*! |
5557 | Looks up the tag name for \a t in the map of metadata |
5558 | values for the current topic in \a inner. If one or more |
5559 | value sfor the tag are found, the elements are written. |
5560 | Otherwise nothing is written. |
5561 | |
5562 | Returns true or false depending on whether it writes |
5563 | at least one element using the tag \a t. |
5564 | |
5565 | \note If \a t is found in the metadata map, it is erased. |
5566 | i.e. Once you call this function for a particular \a t, |
5567 | you consume \a t. |
5568 | */ |
5569 | bool DitaXmlGenerator::writeMetadataElements(const InnerNode* inner, |
5570 | DitaXmlGenerator::DitaTag t) |
5571 | { |
5572 | QStringList s = getMetadataElements(inner,t); |
5573 | if (s.isEmpty()) |
5574 | return false; |
5575 | for (int i=0; i<s.size(); ++i) { |
5576 | writeStartTag(t); |
5577 | xmlWriter().writeCharacters(s[i]); |
5578 | writeEndTag(); |
5579 | } |
5580 | return true; |
5581 | } |
5582 | |
5583 | /*! |
5584 | Looks up the tag name for \a t in the map of metadata |
5585 | values for the current topic in \a inner. If a value |
5586 | for the tag is found, the value is returned. |
5587 | |
5588 | \note If \a t is found in the metadata map, it is erased. |
5589 | i.e. Once you call this function for a particular \a t, |
5590 | you consume \a t. |
5591 | */ |
5592 | QString DitaXmlGenerator::getMetadataElement(const InnerNode* inner, DitaXmlGenerator::DitaTag t) |
5593 | { |
5594 | QString s = Generator::getMetadataElement(inner, ditaTags[t]); |
5595 | if (s.isEmpty()) |
5596 | s = metadataDefault(t); |
5597 | return s; |
5598 | } |
5599 | |
5600 | /*! |
5601 | Looks up the tag name for \a t in the map of metadata |
5602 | values for the current topic in \a inner. If values |
5603 | for the tag are found, they are returned in a string |
5604 | list. |
5605 | |
5606 | \note If \a t is found in the metadata map, all the |
5607 | pairs having the key \a t are erased. i.e. Once you |
5608 | all this function for a particular \a t, you consume |
5609 | \a t. |
5610 | */ |
5611 | QStringList DitaXmlGenerator::getMetadataElements(const InnerNode* inner, |
5612 | DitaXmlGenerator::DitaTag t) |
5613 | { |
5614 | QStringList s = Generator::getMetadataElements(inner,ditaTags[t]); |
5615 | if (s.isEmpty()) |
5616 | s.append(metadataDefault(t)); |
5617 | return s; |
5618 | } |
5619 | |
5620 | /*! |
5621 | Returns the value of key \a t or an empty string |
5622 | if \a t is not found in the map. |
5623 | */ |
5624 | QString DitaXmlGenerator::metadataDefault(DitaTag t) const |
5625 | { |
5626 | return metadataDefaults.value(ditaTags[t]); |
5627 | } |
5628 | |
5629 | /*! |
5630 | Writes the <prolog> element for the \a inner node |
5631 | using the \a marker. The <prolog> element contains |
5632 | the <metadata> element, plus some others. This |
5633 | function writes one or more of these elements: |
5634 | |
5635 | \list |
5636 | \o <audience> * |
5637 | \o <author> * |
5638 | \o <brand> not used |
5639 | \o <category> * |
5640 | \o <compomnent> * |
5641 | \o <copyrholder> * |
5642 | \o <copyright> * |
5643 | \o <created> not used |
5644 | \o <copyryear> * |
5645 | \o <critdates> not used |
5646 | \o <keyword> not used |
5647 | \o <keywords> not used |
5648 | \o <metadata> * |
5649 | \o <othermeta> * |
5650 | \o <permissions> * |
5651 | \o <platform> not used |
5652 | \o <prodinfo> * |
5653 | \o <prodname> * |
5654 | \o <prolog> * |
5655 | \o <publisher> * |
5656 | \o <resourceid> not used |
5657 | \o <revised> not used |
5658 | \o <source> not used |
5659 | \o <tm> not used |
5660 | \o <unknown> not used |
5661 | \o <vrm> * |
5662 | \o <vrmlist> * |
5663 | \endlist |
5664 | |
5665 | \node * means the tag has been used. |
5666 | |
5667 | */ |
5668 | void |
5669 | DitaXmlGenerator::writeProlog(const InnerNode* inner) |
5670 | { |
5671 | if (!inner) |
5672 | return; |
5673 | writeStartTag(DT_prolog); |
5674 | writeMetadataElements(inner,DT_author); |
5675 | writeMetadataElement(inner,DT_publisher); |
5676 | QString s = getMetadataElement(inner,DT_copyryear); |
5677 | QString t = getMetadataElement(inner,DT_copyrholder); |
5678 | writeStartTag(DT_copyright); |
5679 | writeStartTag(DT_copyryear); |
5680 | if (!s.isEmpty()) |
5681 | xmlWriter().writeAttribute("year",s); |
5682 | writeEndTag(); // </copyryear> |
5683 | writeStartTag(DT_copyrholder); |
5684 | if (!s.isEmpty()) |
5685 | xmlWriter().writeCharacters(t); |
5686 | writeEndTag(); // </copyrholder> |
5687 | writeEndTag(); // </copyright> |
5688 | s = getMetadataElement(inner,DT_permissions); |
5689 | writeStartTag(DT_permissions); |
5690 | xmlWriter().writeAttribute("view",s); |
5691 | writeEndTag(); // </permissions> |
5692 | writeStartTag(DT_metadata); |
5693 | QStringList sl = getMetadataElements(inner,DT_audience); |
5694 | if (!sl.isEmpty()) { |
5695 | for (int i=0; i<sl.size(); ++i) { |
5696 | writeStartTag(DT_audience); |
5697 | xmlWriter().writeAttribute("type",sl[i]); |
5698 | writeEndTag(); // </audience> |
5699 | } |
5700 | } |
5701 | if (!writeMetadataElement(inner,DT_category,false)) { |
5702 | writeStartTag(DT_category); |
5703 | QString category = "Page"; |
5704 | if (inner->type() == Node::Class) |
5705 | category = "Class reference"; |
5706 | else if (inner->type() == Node::Namespace) |
5707 | category = "Namespace"; |
5708 | else if (inner->type() == Node::Fake) { |
5709 | if (inner->subType() == Node::QmlClass) |
5710 | category = "QML Element Reference"; |
5711 | else if (inner->subType() == Node::QmlBasicType) |
5712 | category = "QML Basic Type"; |
5713 | else if (inner->subType() == Node::HeaderFile) |
5714 | category = "Header File"; |
5715 | else if (inner->subType() == Node::Module) |
5716 | category = "Module"; |
5717 | else if (inner->subType() == Node::File) |
5718 | category = "Example Source File"; |
5719 | else if (inner->subType() == Node::Example) |
5720 | category = "Example"; |
5721 | else if (inner->subType() == Node::Image) |
5722 | category = "Image"; |
5723 | else if (inner->subType() == Node::Group) |
5724 | category = "Group"; |
5725 | else if (inner->subType() == Node::Page) |
5726 | category = "Page"; |
5727 | else if (inner->subType() == Node::ExternalPage) |
5728 | category = "External Page"; // Is this necessary? |
5729 | } |
5730 | xmlWriter().writeCharacters(category); |
5731 | writeEndTag(); // </category> |
5732 | } |
5733 | if (vrm.size() > 0) { |
5734 | writeStartTag(DT_prodinfo); |
5735 | if (!writeMetadataElement(inner,DT_prodname,false)) { |
5736 | writeStartTag(DT_prodname); |
5737 | xmlWriter().writeCharacters(projectDescription); |
5738 | writeEndTag(); // </prodname> |
5739 | } |
5740 | writeStartTag(DT_vrmlist); |
5741 | writeStartTag(DT_vrm); |
5742 | if (vrm.size() > 0) |
5743 | xmlWriter().writeAttribute("version",vrm[0]); |
5744 | if (vrm.size() > 1) |
5745 | xmlWriter().writeAttribute("release",vrm[1]); |
5746 | if (vrm.size() > 2) |
5747 | xmlWriter().writeAttribute("modification",vrm[2]); |
5748 | writeEndTag(); // <vrm> |
5749 | writeEndTag(); // <vrmlist> |
5750 | if (!writeMetadataElement(inner,DT_component,false)) { |
5751 | QString component = inner->moduleName(); |
5752 | if (!component.isEmpty()) { |
5753 | writeStartTag(DT_component); |
5754 | xmlWriter().writeCharacters(component); |
5755 | writeEndTag(); // </component> |
5756 | } |
5757 | } |
5758 | writeEndTag(); // </prodinfo> |
5759 | } |
5760 | const QStringMultiMap& metaTagMap = inner->doc().metaTagMap(); |
5761 | QMapIterator<QString, QString> i(metaTagMap); |
5762 | while (i.hasNext()) { |
5763 | i.next(); |
5764 | writeStartTag(DT_othermeta); |
5765 | xmlWriter().writeAttribute("name",i.key()); |
5766 | xmlWriter().writeAttribute("content",i.value()); |
5767 | writeEndTag(); // </othermeta> |
5768 | } |
5769 | writeEndTag(); // </metadata> |
5770 | writeEndTag(); // </prolog> |
5771 | } |
5772 | |
5773 | QT_END_NAMESPACE |
5774 |
Warning: That file was not part of the compilation database. It may have many parsing errors.