PageRenderTime 29ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/src/qt/qtbase/src/tools/qdoc/ditaxmlgenerator.cpp

https://gitlab.com/x33n/phantomjs
C++ | 1460 lines | 1224 code | 50 blank | 186 comment | 339 complexity | 7f93625b0b1c8f0e55c7c649f3225dd2 MD5 | raw file
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2013 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. ditaxmlgenerator.cpp
  43. */
  44. #include <qdebug.h>
  45. #include <qlist.h>
  46. #include <qiterator.h>
  47. #include <qtextcodec.h>
  48. #include <quuid.h>
  49. #include "codemarker.h"
  50. #include "codeparser.h"
  51. #include "ditaxmlgenerator.h"
  52. #include "node.h"
  53. #include "quoter.h"
  54. #include "separator.h"
  55. #include "tree.h"
  56. #include <ctype.h>
  57. #include "qdocdatabase.h"
  58. QT_BEGIN_NAMESPACE
  59. #define COMMAND_VERSION Doc::alias("version")
  60. int DitaXmlGenerator::id = 0;
  61. /*
  62. The strings in this array must appear in the same order as
  63. the values in enum DitaXmlGenerator::DitaTag.
  64. */
  65. QString DitaXmlGenerator::ditaTags[] =
  66. {
  67. "",
  68. "alt",
  69. "apiData",
  70. "apiDef",
  71. "apiDefItem",
  72. "apiDesc",
  73. "apiDetail",
  74. "apiItemName",
  75. "APIMap",
  76. "apiName",
  77. "apiRef",
  78. "apiRelation",
  79. "audience",
  80. "author",
  81. "b",
  82. "body",
  83. "bodydiv",
  84. "brand",
  85. "category",
  86. "codeblock",
  87. "colspec",
  88. "comment",
  89. "component",
  90. "copyrholder",
  91. "copyright",
  92. "copyryear",
  93. "created",
  94. "critdates",
  95. "cxxAPIMap",
  96. "cxxClass",
  97. "cxxClassAbstract",
  98. "cxxClassAccessSpecifier",
  99. "cxxClassAPIItemLocation",
  100. "cxxClassBaseClass",
  101. "cxxClassDeclarationFile",
  102. "cxxClassDeclarationFileLine",
  103. "cxxClassDeclarationFileLineStart",
  104. "cxxClassDeclarationFileLineEnd",
  105. "cxxClassDefinition",
  106. "cxxClassDerivation",
  107. "cxxClassDerivationAccessSpecifier",
  108. "cxxClassDerivations",
  109. "cxxClassDetail",
  110. "cxxClassNested",
  111. "cxxClassNestedClass",
  112. "cxxClassNestedDetail",
  113. "cxxDefine",
  114. "cxxDefineAccessSpecifier",
  115. "cxxDefineAPIItemLocation",
  116. "cxxDefineDeclarationFile",
  117. "cxxDefineDeclarationFileLine",
  118. "cxxDefineDefinition",
  119. "cxxDefineDetail",
  120. "cxxDefineNameLookup",
  121. "cxxDefineParameter",
  122. "cxxDefineParameterDeclarationName",
  123. "cxxDefineParameters",
  124. "cxxDefinePrototype",
  125. "cxxDefineReimplemented",
  126. "cxxEnumeration",
  127. "cxxEnumerationAccessSpecifier",
  128. "cxxEnumerationAPIItemLocation",
  129. "cxxEnumerationDeclarationFile",
  130. "cxxEnumerationDeclarationFileLine",
  131. "cxxEnumerationDeclarationFileLineStart",
  132. "cxxEnumerationDeclarationFileLineEnd",
  133. "cxxEnumerationDefinition",
  134. "cxxEnumerationDetail",
  135. "cxxEnumerationNameLookup",
  136. "cxxEnumerationPrototype",
  137. "cxxEnumerationScopedName",
  138. "cxxEnumerator",
  139. "cxxEnumeratorInitialiser",
  140. "cxxEnumeratorNameLookup",
  141. "cxxEnumeratorPrototype",
  142. "cxxEnumerators",
  143. "cxxEnumeratorScopedName",
  144. "cxxFunction",
  145. "cxxFunctionAccessSpecifier",
  146. "cxxFunctionAPIItemLocation",
  147. "cxxFunctionConst",
  148. "cxxFunctionConstructor",
  149. "cxxFunctionDeclarationFile",
  150. "cxxFunctionDeclarationFileLine",
  151. "cxxFunctionDeclaredType",
  152. "cxxFunctionDefinition",
  153. "cxxFunctionDestructor",
  154. "cxxFunctionDetail",
  155. "cxxFunctionNameLookup",
  156. "cxxFunctionParameter",
  157. "cxxFunctionParameterDeclarationName",
  158. "cxxFunctionParameterDeclaredType",
  159. "cxxFunctionParameterDefaultValue",
  160. "cxxFunctionParameters",
  161. "cxxFunctionPrototype",
  162. "cxxFunctionPureVirtual",
  163. "cxxFunctionReimplemented",
  164. "cxxFunctionScopedName",
  165. "cxxFunctionStorageClassSpecifierStatic",
  166. "cxxFunctionVirtual",
  167. "cxxTypedef",
  168. "cxxTypedefAccessSpecifier",
  169. "cxxTypedefAPIItemLocation",
  170. "cxxTypedefDeclarationFile",
  171. "cxxTypedefDeclarationFileLine",
  172. "cxxTypedefDefinition",
  173. "cxxTypedefDetail",
  174. "cxxTypedefNameLookup",
  175. "cxxTypedefScopedName",
  176. "cxxVariable",
  177. "cxxVariableAccessSpecifier",
  178. "cxxVariableAPIItemLocation",
  179. "cxxVariableDeclarationFile",
  180. "cxxVariableDeclarationFileLine",
  181. "cxxVariableDeclaredType",
  182. "cxxVariableDefinition",
  183. "cxxVariableDetail",
  184. "cxxVariableNameLookup",
  185. "cxxVariablePrototype",
  186. "cxxVariableReimplemented",
  187. "cxxVariableScopedName",
  188. "cxxVariableStorageClassSpecifierStatic",
  189. "data",
  190. "data-about",
  191. "dd",
  192. "dl",
  193. "dlentry",
  194. "dt",
  195. "entry",
  196. "fig",
  197. "i",
  198. "image",
  199. "keyword",
  200. "keywords",
  201. "li",
  202. "link",
  203. "linktext",
  204. "lq",
  205. "map",
  206. "mapref",
  207. "metadata",
  208. "note",
  209. "ol",
  210. "othermeta",
  211. "p",
  212. "parameter",
  213. "permissions",
  214. "ph",
  215. "platform",
  216. "pre",
  217. "prodinfo",
  218. "prodname",
  219. "prolog",
  220. "publisher",
  221. "qmlAttached",
  222. "qmlDetail",
  223. "qmlImportModule",
  224. "qmlInheritedBy",
  225. "qmlInherits",
  226. "qmlInstantiates",
  227. "qmlMethod",
  228. "qmlMethodDef",
  229. "qmlMethodDetail",
  230. "qmlName",
  231. "qmlProperty",
  232. "qmlPropertyDef",
  233. "qmlPropertyDetail",
  234. "qmlPropertyGroup",
  235. "qmlPropertyGroupDef",
  236. "qmlPropertyGroupDetail",
  237. "qmlQualifier",
  238. "qmlSignal",
  239. "qmlSignalDef",
  240. "qmlSignalDetail",
  241. "qmlSignalHandler",
  242. "qmlSignalHandlerDef",
  243. "qmlSignalHandlerDetail",
  244. "qmlSignature",
  245. "qmlSince",
  246. "qmlType",
  247. "qmlTypeDef",
  248. "qmlTypeDetail",
  249. "related-links",
  250. "resourceid",
  251. "revised",
  252. "row",
  253. "section",
  254. "sectiondiv",
  255. "shortdesc",
  256. "simpletable",
  257. "source",
  258. "stentry",
  259. "sthead",
  260. "strow",
  261. "sub",
  262. "sup",
  263. "table",
  264. "tbody",
  265. "tgroup",
  266. "thead",
  267. "title",
  268. "tm",
  269. "topic",
  270. "topicmeta",
  271. "topicref",
  272. "tt",
  273. "u",
  274. "uicontrol",
  275. "ul",
  276. "unknown",
  277. "vrm",
  278. "vrmlist",
  279. "xref",
  280. ""
  281. };
  282. /*!
  283. Composes a string to be used as an href attribute in DITA
  284. XML. It is composed of the file name and the UUID separated
  285. by a '#'. If this node is a class node, the file name is
  286. taken from this node; if this node is a function node, the
  287. file name is taken from the parent node of this node.
  288. */
  289. QString DitaXmlGenerator::ditaXmlHref(Node* n)
  290. {
  291. QString href;
  292. if ((n->type() == Node::Function) ||
  293. (n->type() == Node::Property) ||
  294. (n->type() == Node::Variable)) {
  295. href = fileBase(n->parent());
  296. }
  297. else {
  298. href = fileBase(n);
  299. }
  300. if (!href.endsWith(".xml") && !href.endsWith(".dita"))
  301. href += ".dita";
  302. return href + QLatin1Char('#') + n->guid();
  303. }
  304. void DitaXmlGenerator::debugPara(const QString& t)
  305. {
  306. writeStartTag(DT_p);
  307. xmlWriter().writeAttribute("outputclass",t);
  308. xmlWriter().writeCharacters(t);
  309. writeEndTag(); // </p>
  310. }
  311. static bool showBrokenLinks = false;
  312. /*!
  313. Quick, dirty, and very ugly. Unescape \a text
  314. so QXmlStreamWriter::writeCharacters() can put
  315. the escapes back in again!
  316. */
  317. void DitaXmlGenerator::writeCharacters(const QString& text)
  318. {
  319. QString t = text;
  320. t = t.replace("&lt;","<");
  321. t = t.replace("&gt;",">");
  322. t = t.replace("&amp;","&");
  323. t = t.replace("&quot;","\"");
  324. xmlWriter().writeCharacters(t);
  325. }
  326. /*!
  327. Appends an <xref> element to the current XML stream
  328. with the \a href attribute and the \a text.
  329. */
  330. void DitaXmlGenerator::addLink(const QString& href,
  331. const QStringRef& text,
  332. DitaTag t)
  333. {
  334. if (!href.isEmpty()) {
  335. writeStartTag(t);
  336. // formathtml
  337. writeHrefAttribute(href);
  338. writeCharacters(text.toString());
  339. writeEndTag(); // </t>
  340. }
  341. else {
  342. writeCharacters(text.toString());
  343. }
  344. }
  345. /*!
  346. Push \a t onto the dita tag stack and write the appropriate
  347. start tag to the DITA XML file.
  348. */
  349. void DitaXmlGenerator::writeStartTag(DitaTag t)
  350. {
  351. xmlWriter().writeStartElement(ditaTags[t]);
  352. tagStack.push(t);
  353. }
  354. /*!
  355. Pop the current DITA tag off the stack, and write the
  356. appropriate end tag to the DITA XML file. If \a t is
  357. not \e DT_NONE (default), then \a t contains the enum
  358. value of the tag that should be on top of the stack.
  359. If the stack is empty, no end tag is written and false
  360. is returned. Otherwise, an end tag is written and true
  361. is returned.
  362. */
  363. bool DitaXmlGenerator::writeEndTag(DitaTag t)
  364. {
  365. if (tagStack.isEmpty())
  366. return false;
  367. DitaTag top = tagStack.pop();
  368. if (t > DT_NONE && top != t)
  369. qDebug() << "Expected:" << t << "ACTUAL:" << top;
  370. xmlWriter().writeEndElement();
  371. return true;
  372. }
  373. /*!
  374. Return the current DITA element tag, the one
  375. on top of the stack.
  376. */
  377. DitaXmlGenerator::DitaTag DitaXmlGenerator::currentTag()
  378. {
  379. return tagStack.top();
  380. }
  381. /*!
  382. Write the start \a tag. if \a title is not empty, generate
  383. a GUID from it and write the GUID as the value of the \e{id}
  384. attribute.
  385. Then if \a outputclass is not empty, write it as the value
  386. of the \a outputclass attribute.
  387. Fiunally, set the section nesting level to 1 and return 1.
  388. */
  389. int DitaXmlGenerator::enterDesc(DitaTag tag, const QString& outputclass, const QString& title)
  390. {
  391. writeStartTag(tag);
  392. if (!title.isEmpty()) {
  393. writeGuidAttribute(title);
  394. //Are there cases where the spectitle is required?
  395. //xmlWriter().writeAttribute("spectitle",title);
  396. }
  397. if (!outputclass.isEmpty())
  398. xmlWriter().writeAttribute("outputclass",outputclass);
  399. sectionNestingLevel = 1;
  400. return sectionNestingLevel;
  401. }
  402. /*!
  403. If the section nesting level is 0, output a \c{<section>}
  404. element with an \e id attribute generated from \a title and
  405. an \e outputclass attribute set to \a outputclass.
  406. If \a title is null, no \e id attribute is output.
  407. If \a outputclass is empty, no \e outputclass attribute
  408. is output.
  409. Finally, increment the section nesting level and return
  410. the new value.
  411. */
  412. int DitaXmlGenerator::enterSection(const QString& outputclass, const QString& title)
  413. {
  414. if (sectionNestingLevel == 0) {
  415. writeStartTag(DT_section);
  416. if (!title.isEmpty())
  417. writeGuidAttribute(title);
  418. if (!outputclass.isEmpty())
  419. xmlWriter().writeAttribute("outputclass",outputclass);
  420. }
  421. else if (!title.isEmpty()) {
  422. writeStartTag(DT_p);
  423. writeGuidAttribute(title);
  424. if (!outputclass.isEmpty())
  425. xmlWriter().writeAttribute("outputclass",outputclass);
  426. writeCharacters(title);
  427. writeEndTag(); // </p>
  428. }
  429. return ++sectionNestingLevel;
  430. }
  431. /*!
  432. If the section nesting level is greater than 0, decrement
  433. it. If it becomes 0, output a \c {</section>}. Return the
  434. decremented section nesting level.
  435. */
  436. int DitaXmlGenerator::leaveSection()
  437. {
  438. if (sectionNestingLevel > 0) {
  439. --sectionNestingLevel;
  440. if (sectionNestingLevel == 0)
  441. writeEndTag(); // </section> or </apiDesc>
  442. }
  443. return sectionNestingLevel;
  444. }
  445. /*!
  446. Constructs the DITA XML output generator.
  447. */
  448. DitaXmlGenerator::DitaXmlGenerator()
  449. : inDetailedDescription(false),
  450. inLegaleseText(false),
  451. inObsoleteLink(false),
  452. inTableBody(false),
  453. noLinks(false),
  454. obsoleteLinks(false),
  455. divNestingLevel(0),
  456. sectionNestingLevel(0),
  457. tableColumnCount(0),
  458. funcLeftParen("\\S(\\()"),
  459. nodeTypeMaps(Node::LastType,0),
  460. nodeSubtypeMaps(Node::LastSubtype,0),
  461. pageTypeMaps(Node::OnBeyondZebra,0)
  462. {
  463. }
  464. /*!
  465. Destroys the DITA XML output generator.
  466. */
  467. DitaXmlGenerator::~DitaXmlGenerator()
  468. {
  469. GuidMaps::iterator i = guidMaps.begin();
  470. while (i != guidMaps.end()) {
  471. delete i.value();
  472. ++i;
  473. }
  474. }
  475. /*!
  476. Initializes the DITA XML output generator's data structures
  477. from the configuration class \a config.
  478. */
  479. void DitaXmlGenerator::initializeGenerator(const Config &config)
  480. {
  481. Generator::initializeGenerator(config);
  482. obsoleteLinks = config.getBool(CONFIG_OBSOLETELINKS);
  483. setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
  484. style = config.getString(DitaXmlGenerator::format() +
  485. Config::dot +
  486. DITAXMLGENERATOR_STYLE);
  487. postHeader = config.getString(DitaXmlGenerator::format() +
  488. Config::dot +
  489. DITAXMLGENERATOR_POSTHEADER);
  490. postPostHeader = config.getString(DitaXmlGenerator::format() +
  491. Config::dot +
  492. DITAXMLGENERATOR_POSTPOSTHEADER);
  493. footer = config.getString(DitaXmlGenerator::format() +
  494. Config::dot +
  495. DITAXMLGENERATOR_FOOTER);
  496. address = config.getString(DitaXmlGenerator::format() +
  497. Config::dot +
  498. DITAXMLGENERATOR_ADDRESS);
  499. pleaseGenerateMacRef = config.getBool(DitaXmlGenerator::format() +
  500. Config::dot +
  501. DITAXMLGENERATOR_GENERATEMACREFS);
  502. project = config.getString(CONFIG_PROJECT);
  503. projectDescription = config.getString(CONFIG_DESCRIPTION);
  504. if (projectDescription.isEmpty() && !project.isEmpty())
  505. projectDescription = project + " Reference Documentation";
  506. projectUrl = config.getString(CONFIG_URL);
  507. tagFile_ = config.getString(CONFIG_TAGFILE);
  508. #ifndef QT_NO_TEXTCODEC
  509. outputEncoding = config.getString(CONFIG_OUTPUTENCODING);
  510. if (outputEncoding.isEmpty())
  511. outputEncoding = QLatin1String("ISO-8859-1");
  512. outputCodec = QTextCodec::codecForName(outputEncoding.toLocal8Bit());
  513. #endif
  514. naturalLanguage = config.getString(CONFIG_NATURALLANGUAGE);
  515. if (naturalLanguage.isEmpty())
  516. naturalLanguage = QLatin1String("en");
  517. config.subVarsAndValues("dita.metadata.default",metadataDefaults);
  518. QSet<QString> editionNames = config.subVars(CONFIG_EDITION);
  519. QSet<QString>::ConstIterator edition = editionNames.constBegin();
  520. while (edition != editionNames.constEnd()) {
  521. QString editionName = *edition;
  522. QStringList editionModules = config.getStringList(CONFIG_EDITION +
  523. Config::dot +
  524. editionName +
  525. Config::dot +
  526. "modules");
  527. QStringList editionGroups = config.getStringList(CONFIG_EDITION +
  528. Config::dot +
  529. editionName +
  530. Config::dot +
  531. "groups");
  532. if (!editionModules.isEmpty())
  533. editionModuleMap[editionName] = editionModules;
  534. if (!editionGroups.isEmpty())
  535. editionGroupMap[editionName] = editionGroups;
  536. ++edition;
  537. }
  538. stylesheets = config.getStringList(DitaXmlGenerator::format() +
  539. Config::dot +
  540. DITAXMLGENERATOR_STYLESHEETS);
  541. customHeadElements = config.getStringList(DitaXmlGenerator::format() +
  542. Config::dot +
  543. DITAXMLGENERATOR_CUSTOMHEADELEMENTS);
  544. version = config.getString(CONFIG_VERSION);
  545. vrm = version.split(QLatin1Char('.'));
  546. }
  547. /*!
  548. Gracefully terminates the DITA XML output generator.
  549. */
  550. void DitaXmlGenerator::terminateGenerator()
  551. {
  552. Generator::terminateGenerator();
  553. }
  554. /*!
  555. Returns "DITAXML".
  556. */
  557. QString DitaXmlGenerator::format()
  558. {
  559. return "DITAXML";
  560. }
  561. /*!
  562. Calls lookupGuid() to get a GUID for \a text, then writes
  563. it to the XML stream as an "id" attribute, and returns it.
  564. */
  565. QString DitaXmlGenerator::writeGuidAttribute(QString text)
  566. {
  567. QString guid = lookupGuid(outFileName(),text);
  568. xmlWriter().writeAttribute("id",guid);
  569. return guid;
  570. }
  571. /*!
  572. Write's the GUID for the \a node to the current XML stream
  573. as an "id" attribute. If the \a node doesn't yet have a GUID,
  574. one is generated.
  575. */
  576. void DitaXmlGenerator::writeGuidAttribute(Node* node)
  577. {
  578. xmlWriter().writeAttribute("id",node->guid());
  579. }
  580. /*!
  581. Looks up \a text in the GUID map. If it finds \a text,
  582. it returns the associated GUID. Otherwise it inserts
  583. \a text into the map with a new GUID, and it returns
  584. the new GUID.
  585. */
  586. QString DitaXmlGenerator::lookupGuid(QString text)
  587. {
  588. QMap<QString, QString>::const_iterator i = name2guidMap.constFind(text);
  589. if (i != name2guidMap.constEnd())
  590. return i.value();
  591. QString guid = Node::cleanId(text);
  592. name2guidMap.insert(text,guid);
  593. return guid;
  594. }
  595. /*!
  596. First, look up the GUID map for \a fileName. If there isn't
  597. a GUID map for \a fileName, create one and insert it into
  598. the map of GUID maps. Then look up \a text in that GUID map.
  599. If \a text is found, return the associated GUID. Otherwise,
  600. insert \a text into the GUID map with a new GUID, and return
  601. the new GUID.
  602. */
  603. QString DitaXmlGenerator::lookupGuid(const QString& fileName, const QString& text)
  604. {
  605. GuidMap* gm = lookupGuidMap(fileName);
  606. GuidMap::const_iterator i = gm->constFind(text);
  607. if (i != gm->constEnd())
  608. return i.value();
  609. QString guid = Node::cleanId(text);
  610. gm->insert(text,guid);
  611. return guid;
  612. }
  613. /*!
  614. Looks up \a fileName in the map of GUID maps. If it finds
  615. \a fileName, it returns a pointer to the associated GUID
  616. map. Otherwise it creates a new GUID map and inserts it
  617. into the map of GUID maps with \a fileName as its key.
  618. */
  619. GuidMap* DitaXmlGenerator::lookupGuidMap(const QString& fileName)
  620. {
  621. GuidMaps::const_iterator i = guidMaps.constFind(fileName);
  622. if (i != guidMaps.constEnd())
  623. return i.value();
  624. GuidMap* gm = new GuidMap;
  625. guidMaps.insert(fileName,gm);
  626. return gm;
  627. }
  628. /*!
  629. Traverses the database generating all the DITA XML documentation.
  630. */
  631. void DitaXmlGenerator::generateTree()
  632. {
  633. qdb_->buildCollections();
  634. if (!runPrepareOnly()) {
  635. Generator::generateTree();
  636. generateCollisionPages();
  637. }
  638. if (!runGenerateOnly()) {
  639. QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-'));
  640. qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index",
  641. projectUrl,
  642. projectDescription,
  643. this,
  644. true);
  645. }
  646. if (!runPrepareOnly()) {
  647. writeDitaMap();
  648. /*
  649. Generate the XML tag file, if it was requested.
  650. */
  651. qdb_->generateTagFile(tagFile_, this);
  652. }
  653. }
  654. static int countTableColumns(const Atom* t)
  655. {
  656. int result = 0;
  657. if (t->type() == Atom::TableHeaderLeft) {
  658. while (t->type() == Atom::TableHeaderLeft) {
  659. int count = 0;
  660. t = t->next();
  661. while (t->type() != Atom::TableHeaderRight) {
  662. if (t->type() == Atom::TableItemLeft) {
  663. for (int i=0; i<t->count(); ++i) {
  664. QString attr = t->string(i);
  665. if (!attr.contains('=')) {
  666. QStringList spans = attr.split(QLatin1Char(','));
  667. if (spans.size() == 2) {
  668. count += spans[0].toInt();
  669. }
  670. else {
  671. ++count;
  672. }
  673. }
  674. }
  675. }
  676. t = t->next();
  677. }
  678. if (count > result)
  679. result = count;
  680. t = t->next();
  681. }
  682. }
  683. else if (t->type() == Atom::TableRowLeft) {
  684. while (t->type() != Atom::TableRowRight) {
  685. if (t->type() == Atom::TableItemLeft) {
  686. for (int i=0; i<t->count(); ++i) {
  687. QString attr = t->string(i);
  688. if (!attr.contains('=')) {
  689. QStringList spans = attr.split(QLatin1Char(','));
  690. if (spans.size() == 2) {
  691. result += spans[0].toInt();
  692. }
  693. else {
  694. ++result;
  695. }
  696. }
  697. }
  698. }
  699. t = t->next();
  700. }
  701. }
  702. return result;
  703. }
  704. /*!
  705. Generate html from an instance of Atom.
  706. */
  707. int DitaXmlGenerator::generateAtom(const Atom *atom,
  708. const Node *relative,
  709. CodeMarker *marker)
  710. {
  711. int skipAhead = 0;
  712. QString hx, str;
  713. static bool in_para = false;
  714. QString guid, hc, attr;
  715. switch (atom->type()) {
  716. case Atom::AbstractLeft:
  717. break;
  718. case Atom::AbstractRight:
  719. break;
  720. case Atom::AutoLink:
  721. if (!noLinks && !inLink_ && !inContents_ && !inSectionHeading_) {
  722. const Node* node = 0;
  723. QString link = getLink(atom, relative, &node);
  724. if (!link.isEmpty()) {
  725. beginLink(link);
  726. generateLink(atom, marker);
  727. endLink();
  728. }
  729. else {
  730. writeCharacters(protectEnc(atom->string()));
  731. }
  732. }
  733. else {
  734. writeCharacters(protectEnc(atom->string()));
  735. }
  736. break;
  737. case Atom::BaseName:
  738. break;
  739. case Atom::BriefLeft:
  740. {
  741. Node::Type t = relative->type();
  742. if (inSection()) {
  743. in_para = true;
  744. writeStartTag(DT_p);
  745. xmlWriter().writeAttribute("outputclass","brief");
  746. }
  747. else {
  748. noLinks = true;
  749. writeStartTag(DT_shortdesc);
  750. }
  751. if (t == Node::Property || t == Node::Variable) {
  752. xmlWriter().writeCharacters("This ");
  753. if (relative->type() == Node::Property)
  754. xmlWriter().writeCharacters("property");
  755. else if (relative->type() == Node::Variable)
  756. xmlWriter().writeCharacters("variable");
  757. xmlWriter().writeCharacters(" holds ");
  758. }
  759. if (noLinks) {
  760. atom = atom->next();
  761. while (atom != 0 && atom->type() != Atom::BriefRight) {
  762. if (atom->type() == Atom::String ||
  763. atom->type() == Atom::AutoLink)
  764. str += atom->string();
  765. skipAhead++;
  766. atom = atom->next();
  767. }
  768. if (t == Node::Property || t == Node::Variable)
  769. str[0] = str[0].toLower();
  770. if (str.endsWith(QLatin1Char('.')))
  771. str.truncate(str.length() - 1);
  772. writeCharacters(str + QLatin1Char('.'));
  773. }
  774. }
  775. break;
  776. case Atom::BriefRight:
  777. // if (relative->type() != Node::Document)
  778. writeEndTag(); // </shortdesc> or </p>
  779. if (in_para)
  780. in_para = false;
  781. noLinks = false;
  782. break;
  783. case Atom::C:
  784. writeStartTag(DT_tt);
  785. if (inLink_) {
  786. writeCharacters(protectEnc(plainCode(atom->string())));
  787. }
  788. else {
  789. writeText(atom->string(), relative);
  790. }
  791. writeEndTag(); // see writeStartElement() above
  792. break;
  793. case Atom::Code:
  794. {
  795. writeStartTag(DT_codeblock);
  796. xmlWriter().writeAttribute("outputclass","cpp");
  797. writeCharacters("\n");
  798. writeText(trimmedTrailing(atom->string()), relative);
  799. writeEndTag(); // </codeblock>
  800. }
  801. break;
  802. case Atom::Qml:
  803. writeStartTag(DT_codeblock);
  804. xmlWriter().writeAttribute("outputclass","qml");
  805. writeCharacters("\n");
  806. writeText(trimmedTrailing(atom->string()), relative);
  807. writeEndTag(); // </codeblock>
  808. break;
  809. case Atom::CodeNew:
  810. writeStartTag(DT_p);
  811. xmlWriter().writeCharacters("you can rewrite it as");
  812. writeEndTag(); // </p>
  813. writeStartTag(DT_codeblock);
  814. writeCharacters("\n");
  815. writeText(trimmedTrailing(atom->string()), relative);
  816. writeEndTag(); // </codeblock>
  817. break;
  818. case Atom::CodeOld:
  819. writeStartTag(DT_p);
  820. xmlWriter().writeCharacters("For example, if you have code like");
  821. writeEndTag(); // </p>
  822. // fallthrough
  823. case Atom::CodeBad:
  824. writeStartTag(DT_codeblock);
  825. writeCharacters("\n");
  826. writeCharacters(trimmedTrailing(plainCode(atom->string())));
  827. writeEndTag(); // </codeblock>
  828. break;
  829. case Atom::DivLeft:
  830. {
  831. bool inStartElement = false;
  832. attr = atom->string();
  833. DitaTag t = currentTag();
  834. if ((t == DT_section) || (t == DT_sectiondiv)) {
  835. writeStartTag(DT_sectiondiv);
  836. divNestingLevel++;
  837. inStartElement = true;
  838. }
  839. else if ((t == DT_body) || (t == DT_bodydiv)) {
  840. writeStartTag(DT_bodydiv);
  841. divNestingLevel++;
  842. inStartElement = true;
  843. }
  844. if (!attr.isEmpty()) {
  845. if (attr.contains('=')) {
  846. int index = 0;
  847. int from = 0;
  848. QString values;
  849. while (index >= 0) {
  850. index = attr.indexOf('"',from);
  851. if (index >= 0) {
  852. ++index;
  853. from = index;
  854. index = attr.indexOf('"',from);
  855. if (index > from) {
  856. if (!values.isEmpty())
  857. values.append(' ');
  858. values += attr.mid(from,index-from);
  859. from = index+1;
  860. }
  861. }
  862. }
  863. attr = values;
  864. }
  865. }
  866. if (inStartElement)
  867. xmlWriter().writeAttribute("outputclass", attr);
  868. }
  869. break;
  870. case Atom::DivRight:
  871. if ((currentTag() == DT_sectiondiv) || (currentTag() == DT_bodydiv)) {
  872. writeEndTag(); // </sectiondiv>, </bodydiv>, or </p>
  873. if (divNestingLevel > 0)
  874. --divNestingLevel;
  875. }
  876. break;
  877. case Atom::FootnoteLeft:
  878. // ### For now
  879. if (in_para) {
  880. writeEndTag(); // </p>
  881. in_para = false;
  882. }
  883. xmlWriter().writeCharacters("<!-- ");
  884. break;
  885. case Atom::FootnoteRight:
  886. // ### For now
  887. xmlWriter().writeCharacters("-->");
  888. break;
  889. case Atom::FormatElse:
  890. case Atom::FormatEndif:
  891. case Atom::FormatIf:
  892. break;
  893. case Atom::FormattingLeft:
  894. {
  895. DitaTag t = DT_LAST;
  896. if (atom->string() == ATOM_FORMATTING_BOLD)
  897. t = DT_b;
  898. else if (atom->string() == ATOM_FORMATTING_PARAMETER)
  899. t = DT_i;
  900. else if (atom->string() == ATOM_FORMATTING_ITALIC)
  901. t = DT_i;
  902. else if (atom->string() == ATOM_FORMATTING_TELETYPE)
  903. t = DT_tt;
  904. else if (atom->string().startsWith("span ")) {
  905. t = DT_keyword;
  906. }
  907. else if (atom->string() == ATOM_FORMATTING_UICONTROL)
  908. t = DT_uicontrol;
  909. else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
  910. t = DT_u;
  911. else if (atom->string() == ATOM_FORMATTING_INDEX)
  912. t = DT_comment;
  913. else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
  914. t = DT_sub;
  915. else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
  916. t = DT_sup;
  917. else
  918. qDebug() << "DT_LAST";
  919. writeStartTag(t);
  920. if (atom->string() == ATOM_FORMATTING_PARAMETER) {
  921. if (atom->next() != 0 && atom->next()->type() == Atom::String) {
  922. QRegExp subscriptRegExp("([a-z]+)_([0-9n])");
  923. if (subscriptRegExp.exactMatch(atom->next()->string())) {
  924. xmlWriter().writeCharacters(subscriptRegExp.cap(1));
  925. writeStartTag(DT_sub);
  926. xmlWriter().writeCharacters(subscriptRegExp.cap(2));
  927. writeEndTag(); // </sub>
  928. skipAhead = 1;
  929. }
  930. }
  931. }
  932. else if (t == DT_keyword) {
  933. QString attr = atom->string().mid(5);
  934. if (!attr.isEmpty()) {
  935. if (attr.contains('=')) {
  936. int index = 0;
  937. int from = 0;
  938. QString values;
  939. while (index >= 0) {
  940. index = attr.indexOf('"',from);
  941. if (index >= 0) {
  942. ++index;
  943. from = index;
  944. index = attr.indexOf('"',from);
  945. if (index > from) {
  946. if (!values.isEmpty())
  947. values.append(' ');
  948. values += attr.mid(from,index-from);
  949. from = index+1;
  950. }
  951. }
  952. }
  953. attr = values;
  954. }
  955. }
  956. xmlWriter().writeAttribute("outputclass", attr);
  957. }
  958. }
  959. break;
  960. case Atom::FormattingRight:
  961. if (atom->string() == ATOM_FORMATTING_LINK) {
  962. endLink();
  963. }
  964. else {
  965. writeEndTag(); // ?
  966. }
  967. break;
  968. case Atom::AnnotatedList:
  969. {
  970. DocNode* dn = qdb_->getGroup(atom->string());
  971. if (dn)
  972. generateAnnotatedList(relative, marker, dn->members());
  973. }
  974. break;
  975. case Atom::GeneratedList:
  976. if (atom->string() == "annotatedclasses") {
  977. generateAnnotatedList(relative, marker, qdb_->getCppClasses());
  978. }
  979. else if (atom->string() == "classes") {
  980. generateCompactList(Generic, relative, qdb_->getCppClasses(), true, QStringLiteral("Q"));
  981. }
  982. else if (atom->string() == "qmlclasses") {
  983. generateCompactList(Generic, relative, qdb_->getQmlTypes(), true, QStringLiteral(""));
  984. }
  985. else if (atom->string().contains("classesbymodule")) {
  986. QString arg = atom->string().trimmed();
  987. QString moduleName = atom->string().mid(atom->string().indexOf("classesbymodule") + 15).trimmed();
  988. QDocDatabase* qdb = QDocDatabase::qdocDB();
  989. DocNode* dn = qdb->findModule(moduleName);
  990. if (dn) {
  991. NodeMap m;
  992. dn->getMemberClasses(m);
  993. if (!m.isEmpty()) {
  994. generateAnnotatedList(relative, marker, m);
  995. }
  996. }
  997. }
  998. else if (atom->string() == "classhierarchy") {
  999. generateClassHierarchy(relative, qdb_->getCppClasses());
  1000. }
  1001. else if (atom->string() == "compatclasses") {
  1002. // "compatclasses" is no longer used. Delete this at some point.
  1003. // mws 03/10/2013
  1004. generateCompactList(Generic, relative, qdb_->getCompatibilityClasses(), false, QStringLiteral("Q"));
  1005. }
  1006. else if (atom->string() == "obsoleteclasses") {
  1007. generateCompactList(Generic, relative, qdb_->getObsoleteClasses(), false, QStringLiteral("Q"));
  1008. }
  1009. else if (atom->string() == "obsoleteqmltypes") {
  1010. generateCompactList(Generic, relative, qdb_->getObsoleteQmlTypes(), false, QStringLiteral(""));
  1011. }
  1012. else if (atom->string() == "obsoletecppmembers") {
  1013. generateCompactList(Obsolete, relative, qdb_->getClassesWithObsoleteMembers(), false, QStringLiteral("Q"));
  1014. }
  1015. else if (atom->string() == "obsoleteqmlmembers") {
  1016. generateCompactList(Obsolete, relative, qdb_->getQmlTypesWithObsoleteMembers(), false, QStringLiteral(""));
  1017. }
  1018. else if (atom->string() == "functionindex") {
  1019. generateFunctionIndex(relative);
  1020. }
  1021. else if (atom->string() == "legalese") {
  1022. generateLegaleseList(relative, marker);
  1023. }
  1024. else if (atom->string() == "mainclasses") {
  1025. // "mainclasses" is no longer used. Delete this at some point.
  1026. // mws 03/10/2013
  1027. generateCompactList(Generic, relative, qdb_->getMainClasses(), true, QStringLiteral("Q"));
  1028. }
  1029. else if (atom->string() == "services") {
  1030. // "services" is no longer used. Delete this at some point.
  1031. // mws 03/10/2013
  1032. generateCompactList(Generic, relative, qdb_->getServiceClasses(), false, QStringLiteral("Q"));
  1033. }
  1034. else if (atom->string() == "overviews") {
  1035. generateOverviewList(relative);
  1036. }
  1037. else if (atom->string() == "namespaces") {
  1038. generateAnnotatedList(relative, marker, qdb_->getNamespaces());
  1039. }
  1040. else if (atom->string() == "related") {
  1041. const DocNode *dn = static_cast<const DocNode *>(relative);
  1042. if (dn)
  1043. generateAnnotatedList(dn, marker, dn->members());
  1044. }
  1045. break;
  1046. case Atom::SinceList:
  1047. {
  1048. const NodeMultiMap& nsmap = qdb_->getSinceMap(atom->string());
  1049. const NodeMap& ncmap = qdb_->getClassMap(atom->string());
  1050. const NodeMap& nqcmap = qdb_->getQmlTypeMap(atom->string());
  1051. if (!nsmap.isEmpty()) {
  1052. QList<Section> sections;
  1053. QList<Section>::ConstIterator s;
  1054. for (int i=0; i<LastSinceType; ++i)
  1055. sections.append(Section(sinceTitle(i),QString(),QString(),QString()));
  1056. NodeMultiMap::const_iterator n = nsmap.constBegin();
  1057. while (n != nsmap.constEnd()) {
  1058. const Node* node = n.value();
  1059. switch (node->type()) {
  1060. case Node::Document:
  1061. if (node->subType() == Node::QmlClass) {
  1062. sections[QmlClass].appendMember((Node*)node);
  1063. }
  1064. break;
  1065. case Node::Namespace:
  1066. sections[Namespace].appendMember((Node*)node);
  1067. break;
  1068. case Node::Class:
  1069. sections[Class].appendMember((Node*)node);
  1070. break;
  1071. case Node::Enum:
  1072. sections[Enum].appendMember((Node*)node);
  1073. break;
  1074. case Node::Typedef:
  1075. sections[Typedef].appendMember((Node*)node);
  1076. break;
  1077. case Node::Function: {
  1078. const FunctionNode* fn = static_cast<const FunctionNode*>(node);
  1079. if (fn->isMacro())
  1080. sections[Macro].appendMember((Node*)node);
  1081. else {
  1082. Node* p = fn->parent();
  1083. if (p) {
  1084. if (p->type() == Node::Class)
  1085. sections[MemberFunction].appendMember((Node*)node);
  1086. else if (p->type() == Node::Namespace) {
  1087. if (p->name().isEmpty())
  1088. sections[GlobalFunction].appendMember((Node*)node);
  1089. else
  1090. sections[NamespaceFunction].appendMember((Node*)node);
  1091. }
  1092. else
  1093. sections[GlobalFunction].appendMember((Node*)node);
  1094. }
  1095. else
  1096. sections[GlobalFunction].appendMember((Node*)node);
  1097. }
  1098. break;
  1099. }
  1100. case Node::Property:
  1101. sections[Property].appendMember((Node*)node);
  1102. break;
  1103. case Node::Variable:
  1104. sections[Variable].appendMember((Node*)node);
  1105. break;
  1106. case Node::QmlProperty:
  1107. sections[QmlProperty].appendMember((Node*)node);
  1108. break;
  1109. case Node::QmlSignal:
  1110. sections[QmlSignal].appendMember((Node*)node);
  1111. break;
  1112. case Node::QmlSignalHandler:
  1113. sections[QmlSignalHandler].appendMember((Node*)node);
  1114. break;
  1115. case Node::QmlMethod:
  1116. sections[QmlMethod].appendMember((Node*)node);
  1117. break;
  1118. default:
  1119. break;
  1120. }
  1121. ++n;
  1122. }
  1123. writeStartTag(DT_ul);
  1124. s = sections.constBegin();
  1125. while (s != sections.constEnd()) {
  1126. if (!(*s).members.isEmpty()) {
  1127. QString li = outFileName() + QLatin1Char('#') + Doc::canonicalTitle((*s).name);
  1128. writeXrefListItem(li, (*s).name);
  1129. }
  1130. ++s;
  1131. }
  1132. writeEndTag(); // </ul>
  1133. int idx = 0;
  1134. s = sections.constBegin();
  1135. while (s != sections.constEnd()) {
  1136. if (!(*s).members.isEmpty()) {
  1137. writeStartTag(DT_p);
  1138. writeGuidAttribute(Doc::canonicalTitle((*s).name));
  1139. xmlWriter().writeAttribute("outputclass","h3");
  1140. writeCharacters(protectEnc((*s).name));
  1141. writeEndTag(); // </p>
  1142. if (idx == Class)
  1143. generateCompactList(Generic, 0, ncmap, false, QString("Q"));
  1144. else if (idx == QmlClass)
  1145. generateCompactList(Generic, 0, nqcmap, false, QString("Q"));
  1146. else if (idx == MemberFunction) {
  1147. ParentMaps parentmaps;
  1148. ParentMaps::iterator pmap;
  1149. NodeList::const_iterator i = s->members.constBegin();
  1150. while (i != s->members.constEnd()) {
  1151. Node* p = (*i)->parent();
  1152. pmap = parentmaps.find(p);
  1153. if (pmap == parentmaps.end())
  1154. pmap = parentmaps.insert(p,NodeMultiMap());
  1155. pmap->insert((*i)->name(),(*i));
  1156. ++i;
  1157. }
  1158. pmap = parentmaps.begin();
  1159. while (pmap != parentmaps.end()) {
  1160. NodeList nlist = pmap->values();
  1161. writeStartTag(DT_p);
  1162. xmlWriter().writeCharacters("Class ");
  1163. writeStartTag(DT_xref);
  1164. // formathtml
  1165. xmlWriter().writeAttribute("href",linkForNode(pmap.key(), 0));
  1166. QStringList pieces = pmap.key()->fullName().split("::");
  1167. writeCharacters(protectEnc(pieces.last()));
  1168. writeEndTag(); // </xref>
  1169. xmlWriter().writeCharacters(":");
  1170. writeEndTag(); // </p>
  1171. generateSection(nlist, 0, marker, CodeMarker::Summary);
  1172. ++pmap;
  1173. }
  1174. }
  1175. else {
  1176. generateSection(s->members, 0, marker, CodeMarker::Summary);
  1177. }
  1178. }
  1179. ++idx;
  1180. ++s;
  1181. }
  1182. }
  1183. }
  1184. break;
  1185. case Atom::BR:
  1186. // DITA XML can't do <br>
  1187. break;
  1188. case Atom::HR: //<p outputclass="horizontal-rule" />
  1189. writeStartTag(DT_p);
  1190. xmlWriter().writeAttribute("outputclass","horizontal-rule");
  1191. writeEndTag(); // </p>
  1192. break;
  1193. case Atom::Image:
  1194. case Atom::InlineImage:
  1195. {
  1196. QString fileName = imageFileName(relative, atom->string());
  1197. QString text;
  1198. if (atom->next() != 0)
  1199. text = atom->next()->string();
  1200. if (fileName.isEmpty()) {
  1201. relative->location().warning(tr("Missing image: %1").arg(protectEnc(atom->string())));
  1202. QString images = "images";
  1203. if (!atom->string().isEmpty() && atom->string()[0] != '/')
  1204. images.append(QLatin1Char('/'));
  1205. fileName = images + atom->string();
  1206. }
  1207. if (relative && (relative->type() == Node::Document) &&
  1208. (relative->subType() == Node::Example)) {
  1209. const ExampleNode* cen = static_cast<const ExampleNode*>(relative);
  1210. if (cen->imageFileName().isEmpty()) {
  1211. ExampleNode* en = const_cast<ExampleNode*>(cen);
  1212. en->setImageFileName(fileName);
  1213. }
  1214. }
  1215. if (currentTag() != DT_xref && atom->type() != Atom::InlineImage)
  1216. writeStartTag(DT_fig);
  1217. writeStartTag(DT_image);
  1218. writeHrefAttribute(protectEnc(fileName));
  1219. if (atom->type() == Atom::Image) {
  1220. xmlWriter().writeAttribute("placement","break");
  1221. xmlWriter().writeAttribute("align","center");
  1222. }
  1223. if (!text.isEmpty()) {
  1224. writeStartTag(DT_alt);
  1225. writeCharacters(protectEnc(text));
  1226. writeEndTag(); // </alt>
  1227. }
  1228. writeEndTag(); // </image>
  1229. if (currentTag() != DT_xref && atom->type() != Atom::InlineImage)
  1230. writeEndTag(); // </fig>
  1231. }
  1232. break;
  1233. case Atom::ImageText:
  1234. // nothing
  1235. break;
  1236. case Atom::ImportantLeft:
  1237. writeStartTag(DT_note);
  1238. xmlWriter().writeAttribute("type","important");
  1239. break;
  1240. case Atom::ImportantRight:
  1241. writeEndTag(); // </note>
  1242. break;
  1243. case Atom::NoteLeft:
  1244. writeStartTag(DT_note);
  1245. xmlWriter().writeAttribute("type","note");
  1246. break;
  1247. case Atom::NoteRight:
  1248. writeEndTag(); // </note>
  1249. break;
  1250. case Atom::LegaleseLeft:
  1251. inLegaleseText = true;
  1252. break;
  1253. case Atom::LegaleseRight:
  1254. inLegaleseText = false;
  1255. break;
  1256. case Atom::LineBreak:
  1257. //xmlWriter().writeEmptyElement("br");
  1258. break;
  1259. case Atom::Link:
  1260. {
  1261. const Node *node = 0;
  1262. QString myLink = getLink(atom, relative, &node);
  1263. if (myLink.isEmpty())
  1264. myLink = getCollisionLink(atom);
  1265. if (myLink.isEmpty() && !noLinkErrors())
  1266. relative->doc().location().warning(tr("Can't link to '%1'").arg(atom->string()));
  1267. else if (!inSectionHeading_)
  1268. beginLink(myLink);
  1269. skipAhead = 1;
  1270. }
  1271. break;
  1272. case Atom::GuidLink:
  1273. {
  1274. beginLink(atom->string());
  1275. skipAhead = 1;
  1276. }
  1277. break;
  1278. case Atom::LinkNode:
  1279. {
  1280. const Node* node = CodeMarker::nodeForString(atom->string());
  1281. beginLink(linkForNode(node, relative));
  1282. skipAhead = 1;
  1283. }
  1284. break;
  1285. case Atom::ListLeft:
  1286. if (in_para) {
  1287. writeEndTag(); // </p>
  1288. in_para = false;
  1289. }
  1290. if (atom->string() == ATOM_LIST_BULLET) {
  1291. writeStartTag(DT_ul);
  1292. }
  1293. else if (atom->string() == ATOM_LIST_TAG) {
  1294. writeStartTag(DT_dl);
  1295. }
  1296. else if (atom->string() == ATOM_LIST_VALUE) {
  1297. threeColumnEnumValueTable_ = isThreeColumnEnumValueTable(atom);
  1298. if (threeColumnEnumValueTable_) {
  1299. writeStartTag(DT_simpletable);
  1300. xmlWriter().writeAttribute("outputclass","valuelist");
  1301. writeStartTag(DT_sthead);
  1302. writeStartTag(DT_stentry);
  1303. xmlWriter().writeCharacters("Constant");
  1304. writeEndTag(); // </stentry>
  1305. writeStartTag(DT_stentry);
  1306. xmlWriter().writeCharacters("Value");
  1307. writeEndTag(); // </stentry>
  1308. writeStartTag(DT_stentry);
  1309. xmlWriter().writeCharacters("Description");
  1310. writeEndTag(); // </stentry>
  1311. writeEndTag(); // </sthead>
  1312. }
  1313. else {
  1314. writeStartTag(DT_simpletable);
  1315. xmlWriter().writeAttribute("outputclass","valuelist");
  1316. writeStartTag(DT_sthead);
  1317. writeStartTag(DT_stentry);
  1318. xmlWriter().writeCharacters("Constant");
  1319. writeEndTag(); // </stentry>
  1320. writeStartTag(DT_stentry);
  1321. xmlWriter().writeCharacters("Value");
  1322. writeEndTag(); // </stentry>
  1323. writeEndTag(); // </sthead>
  1324. }
  1325. }
  1326. else {
  1327. writeStartTag(DT_ol);
  1328. if (atom->string() == ATOM_LIST_UPPERALPHA)
  1329. xmlWriter().writeAttribute("outputclass","upperalpha");
  1330. else if (atom->string() == ATOM_LIST_LOWERALPHA)
  1331. xmlWriter().writeAttribute("outputclass","loweralpha");
  1332. else if (atom->string() == ATOM_LIST_UPPERROMAN)
  1333. xmlWriter().writeAttribute("outputclass","upperroman");
  1334. else if (atom->string() == ATOM_LIST_LOWERROMAN)
  1335. xmlWriter().writeAttribute("outputclass","lowerroman");
  1336. else // (atom->string() == ATOM_LIST_NUMERIC)
  1337. xmlWriter().writeAttribute("outputclass","numeric");
  1338. if (atom->next() != 0 && atom->next()->string().toInt() != 1) {
  1339. /*
  1340. This attribute is not supported in DITA, and at the
  1341. moment, including it is causing a validation error
  1342. wherever it is used. I think it is only used in the
  1343. qdoc manual.
  1344. */
  1345. //xmlWriter().writeAttribute("start",atom->next()->string());
  1346. }
  1347. }
  1348. break;
  1349. case Atom::ListItemNumber:
  1350. // nothing
  1351. break;
  1352. case Atom::ListTagLeft:
  1353. if (atom->string() == ATOM_LIST_TAG) {
  1354. writeStartTag(DT_dt);
  1355. }
  1356. else { // (atom->string() == ATOM_LIST_VALUE)
  1357. writeStartTag(DT_strow);
  1358. writeStartTag(DT_stentry);
  1359. writeStartTag(DT_tt);
  1360. writeCharacters(protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),
  1361. relative))));
  1362. writeEndTag(); // </tt>
  1363. writeEndTag(); // </stentry>
  1364. writeStartTag(DT_stentry);
  1365. QString itemValue;
  1366. if (relative->type() == Node::Enum) {
  1367. const EnumNode *enume = static_cast<const EnumNode *>(relative);
  1368. itemValue = enume->itemValue(atom->next()->string());
  1369. }
  1370. if (itemValue.isEmpty())
  1371. xmlWriter().writeCharacters("?");
  1372. else {
  1373. writeStartTag(DT_tt);
  1374. writeCharacters(protectEnc(itemValue));
  1375. writeEndTag(); // </tt>
  1376. }
  1377. skipAhead = 1;
  1378. }
  1379. break;
  1380. case Atom::ListTagRight:
  1381. if (atom->string() == ATOM_LIST_TAG)
  1382. writeEndTag(); // </dt>
  1383. break;
  1384. case Atom::ListItemLeft:
  1385. if (atom->string() == ATOM_LIST_TAG) {
  1386. writeStartTag(DT_dd);
  1387. }
  1388. else if (atom->string() == ATOM_LIST_VALUE) {
  1389. if (threeColumnEnumValueTable_) {
  1390. writeEndTag(); // </stentry>
  1391. writeStartTag(DT_stentry);
  1392. }
  1393. }
  1394. else {
  1395. writeStartTag(DT_li);
  1396. }
  1397. if (matchAhead(atom, Atom::ParaLeft))
  1398. skipAhead = 1;
  1399. break;
  1400. case Atom::ListItemRight:
  1401. if (atom->string() == ATOM_LIST_TAG) {
  1402. writeEndTag(); // </dd>
  1403. }
  1404. else if (atom->string() == ATOM_LIST_VALUE) {
  1405. writeEndTag(); // </stentry>
  1406. wr