Doxygen
Loading...
Searching...
No Matches
qhp.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 1997-2022 Dimitri van Heesch.
3 *
4 * Permission to use, copy, modify, and distribute this software and its
5 * documentation under the terms of the GNU General Public License is hereby
6 * granted. No representations are made about the suitability of this software
7 * for any purpose. It is provided "as is" without express or implied warranty.
8 * See the GNU General Public License for more details.
9 *
10 * Documents produced by Doxygen are derivative works derived from the
11 * input used in their production; they are not affected by this license.
12 */
13
14#include <algorithm>
15#include <memory>
16#include <string.h>
17#include <vector>
18#include <cassert>
19#include <mutex>
20
21#include "config.h"
22#include "debug.h"
23#include "doxygen.h"
24#include "groupdef.h"
25#include "memberdef.h"
26#include "message.h"
27#include "qhp.h"
28#include "textstream.h"
29#include "util.h"
30#include "portable.h"
31#include "language.h"
32#include "version.h"
33
34static std::once_flag g_blankWritten;
35
36
37static inline void writeIndent(TextStream &t,int indent)
38{
40 {
41 for (int i=0;i<indent;i++) t << " ";
42 }
43}
44
46{
47 private:
48 struct Node
49 {
50 enum class Type { Root, Dir, Section };
51 // constructor for root node
52 Node() : type(Type::Root), parent(nullptr) {}
53 // constructor for dir node
54 Node(Node *parent_) : type(Type::Dir), parent(parent_) {}
55 // constructor for section node
56 Node(Node *parent_, const QCString &title_,const QCString &ref_)
57 : type(Type::Section), parent(parent_), title(title_), ref(ref_) {}
59 Node *parent = nullptr;
62 std::vector<std::unique_ptr<Node>> children;
63 };
64
67
68 void traverse(const Node &root,TextStream &t,int indent) const
69 {
70 /* Input: Output:
71 * =================================================
72 * Section1 <section title=".." ref="..">
73 * Dir1
74 * Section2 <section title=".." ref="..">
75 * Dir2
76 * Section3 <section title=".." ref=".."/>
77 * </section>
78 * </section>
79 * Section4 <section title=".." ref="..>
80 * Dir3
81 * Dir4
82 * Section5 <section title=.." ref=.."/>
83 * </section>
84 * Section6 <section title=".." ref=".."/>
85 */
86 size_t numChildren = root.children.size();
87 size_t i=0;
88 while (i<numChildren)
89 {
90 if (root.children[i]->type==Node::Type::Section)
91 {
92 i++;
93 if (i<numChildren && root.children[i]->type==Node::Type::Dir)
94 {
95 // we have a dir node
96 writeIndent(t,indent);
97 t << "<section title=\"" << convertToXML(root.children[i-1]->title) << "\""
98 << " ref=\"" << convertToXML(root.children[i-1]->ref) << "\">\n";
99 while (i<numChildren && root.children[i]->type==Node::Type::Dir)
100 {
101 traverse(*root.children[i].get(),t,indent+1);
102 i++;
103 }
104 writeIndent(t,indent);
105 t << "</section>\n";
106 }
107 else // we have a leaf section node
108 {
109 writeIndent(t,indent);
110 t << "<section title=\"" << convertToXML(root.children[i-1]->title) << "\""
111 << " ref=\"" << convertToXML(root.children[i-1]->ref) << "\"/>\n";
112 }
113 }
114 else // dir without preceding section (no extra indent)
115 {
116 traverse(*root.children[i].get(),t,indent);
117 i++;
118 }
119 }
120 }
121
122 public:
123 void addSection(const QCString &title,const QCString &ref)
124 {
125 m_current->children.push_back(std::make_unique<Node>(m_current,title,ref));
126 }
127 void incLevel()
128 {
129 auto newNode = new Node(m_current);
130 m_current->children.push_back(std::unique_ptr<Node>(newNode));
131 m_current = newNode;
132 }
133 void decLevel()
134 {
135 assert(m_current->parent!=nullptr);
136 if (m_current->parent)
137 {
138 m_current = m_current->parent;
139 }
140 }
141 void writeToc(TextStream &t) const
142 {
143 writeIndent(t,2);
144 t << "<toc>\n";
145 traverse(m_root,t,3);
146 writeIndent(t,2);
147 t << "</toc>\n";
148 }
149};
150
160
162{
163 QCString projectName = Config_getString(PROJECT_NAME);
164 QCString versionText = Config_getString(PROJECT_NUMBER);
165 if (projectName.isEmpty()) projectName="Root";
166 if (!versionText.isEmpty()) projectName+=" "+versionText;
167 return projectName;
168}
169
170static QCString makeFileName(const QCString & withoutExtension)
171{
172 QCString result=withoutExtension;
173 if (!result.isEmpty())
174 {
175 if (result.at(0)=='!') // relative URL -> strip marker
176 {
177 result=result.mid(1);
178 }
179 else // add specified HTML extension
180 {
182 }
183 }
184 return result;
185}
186
187static QCString makeRef(const QCString & withoutExtension, const QCString & anchor)
188{
189 //printf("QHP::makeRef(%s,%s)\n",withoutExtension,anchor);
190 if (withoutExtension.isEmpty()) return QCString();
191 QCString result = makeFileName(withoutExtension);
192 if (anchor.isEmpty()) return result;
193 return result+"#"+anchor;
194}
195
196Qhp::Qhp() : p(std::make_unique<Private>()) {}
197Qhp::~Qhp() = default;
198
200{
201 /*
202 <QtHelpProject version="1.0">
203 <namespace>mycompany.com.myapplication.1_0</namespace>
204 <virtualFolder>doc</virtualFolder>
205 <customFilter name="My Application 1.0">
206 <filterAttribute>myapp</filterAttribute>
207 <filterAttribute>1.0</filterAttribute>
208 </customFilter>
209 <filterSection>
210 <filterAttribute>myapp</filterAttribute>
211 <filterAttribute>1.0</filterAttribute>
212 ..
213 */
214 QCString fileName = Config_getString(HTML_OUTPUT) + "/" + qhpFileName;
215 p->docFile = Portable::openOutputStream(fileName);
216 if (!p->docFile.is_open())
217 {
218 term("Could not open file %s for writing\n", fileName.data());
219 }
220 p->doc.setStream(&p->docFile);
221
222 p->doc << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
223 p->doc << "<QtHelpProject version=\"1.0\">\n";
224 writeIndent(p->doc,1);
225 p->doc << "<namespace>" << convertToXML(Config_getString(QHP_NAMESPACE)) << "</namespace>\n";
226 writeIndent(p->doc,1);
227 p->doc << "<virtualFolder>" << convertToXML(Config_getString(QHP_VIRTUAL_FOLDER)) << "</virtualFolder>\n";
228
229 // Add custom filter
230 QCString filterName = Config_getString(QHP_CUST_FILTER_NAME);
231 if (!filterName.isEmpty())
232 {
233 writeIndent(p->doc,1);
234 p->doc << "<customFilter name=\"" << convertToXML(filterName) << "\">\n";
235
236 StringVector customFilterAttributes =
237 split(Config_getString(QHP_CUST_FILTER_ATTRS).str(), " ");
238 for (const auto &attr : customFilterAttributes)
239 {
240 writeIndent(p->doc,2);
241 p->doc << "<filterAttribute>" << convertToXML(QCString(attr)) << "</filterAttribute>\n";
242 }
243 writeIndent(p->doc,1);
244 p->doc << "</customFilter>\n";
245 }
246
247 writeIndent(p->doc,1);
248 p->doc << "<filterSection>\n";
249
250 // Add section attributes
251 StringVector sectionFilterAttributes = split(Config_getString(QHP_SECT_FILTER_ATTRS).str(), " ");
252 // always add doxygen as filter attribute
253 if (std::find(sectionFilterAttributes.begin(), sectionFilterAttributes.end(), "doxygen") ==
254 sectionFilterAttributes.end())
255 {
256 sectionFilterAttributes.emplace_back("doxygen");
257 }
258 for (const auto &attr : sectionFilterAttributes)
259 {
260 writeIndent(p->doc,2);
261 p->doc << "<filterAttribute>" << convertToXML(QCString(attr)) << "</filterAttribute>\n";
262 }
263
264 // Add extra root node to the TOC
265 p->sectionTree.addSection(getFullProjectName(),"index"+Doxygen::htmlFileExtension);
266 p->sectionTree.incLevel();
267
268 writeIndent(p->index,2);
269 p->index << "<keywords>\n";
270}
271
273{
274 // close root node
275 p->sectionTree.decLevel();
276
277 // Finish TOC
278 p->sectionTree.writeToc(p->doc);
279
280 // Finish index
281 writeIndent(p->index,2);
282 p->index << "</keywords>\n";
283 p->doc << p->index.str();
284
285 // Finish files
286 writeIndent(p->doc,2);
287 p->doc << "<files>\n";
288 for (auto &s : p->files)
289 {
290 writeIndent(p->doc,3);
291 p->doc << s.c_str() << "\n";
292 }
293 writeIndent(p->doc,2);
294 p->doc << "</files>\n";
295
296 writeIndent(p->doc,1);
297 p->doc << "</filterSection>\n";
298 p->doc << "</QtHelpProject>\n";
299
300 p->doc.flush();
301 p->docFile.close();
302}
303
305{
306 p->sectionTree.incLevel();
307}
308
310{
311 p->sectionTree.decLevel();
312}
313
314void Qhp::addContentsItem(bool /* isDir */, const QCString & name,
315 const QCString & /*ref*/, const QCString & file,
316 const QCString &anchor, bool /* separateIndex */,
317 bool /* addToNavIndex */,
318 const Definition * /*def*/)
319{
320 /*
321 <toc>
322 <section title="My Application Manual" ref="index.html">
323 <section title="Chapter 1" ref="doc.html#chapter1"/>
324 <section title="Chapter 2" ref="doc.html#chapter2"/>
325 <section title="Chapter 3" ref="doc.html#chapter3"/>
326 </section>
327 </toc>
328 */
329
330 QCString f = file;
331 if (!f.isEmpty() && f.at(0)=='^') return; // absolute URL not supported
332
333 if (f.isEmpty())
334 {
335 f = "doxygen_blank";
337 std::call_once(g_blankWritten,[this,&f]()
338 {
339 QCString fileName = Config_getString(HTML_OUTPUT) + "/" + f;
340 std::ofstream blankFile = Portable::openOutputStream(fileName); // we just need an empty file
341 if (!blankFile.is_open())
342 {
343 term("Could not open file %s for writing\n", qPrint(fileName));
344 }
345 TextStream blank;
346 blank.setStream(&blankFile);
347 blank << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
348 blank << "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"" + theTranslator->trISOLang() + "\">\n";
349 blank << "<head>\n";
350 blank << "<title>Validator / crawler helper</title>\n";
351 blank << "<meta http-equiv=\"Content-Type\" content=\"text/xhtml;charset=UTF-8\"/>\n";
352 blank << "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=11\"/>\n";
353
354 blank << "<meta name=\"generator\" content=\"Doxygen " + getDoxygenVersion() + "\"/>\n";
355 blank << "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n";
356 blank << "</head>\n";
357 blank << "<body>\n";
358 blank << "</body>\n";
359 blank << "</html>\n";
360 blank.flush();
361 blankFile.close();
362 addFile(f);
363 });
364 }
365 QCString finalRef = makeRef(f, anchor);
366 p->sectionTree.addSection(name,finalRef);
367}
368
369void Qhp::addIndexItem(const Definition *context,const MemberDef *md,
370 const QCString &sectionAnchor,const QCString &word)
371{
372 (void)word;
373 //printf("addIndexItem(%s %s %s\n",
374 // context?context->name().data():"<none>",
375 // md?md->name().data():"<none>",
376 // qPrint(word));
377
378 if (context && md) // member
379 {
380 QCString cfname = md->getOutputFileBase();
381 QCString argStr = md->argsString();
382 QCString level1 = context->name();
383 QCString level2 = !word.isEmpty() ? word : md->name();
384 QCString anchor = !sectionAnchor.isEmpty() ? sectionAnchor : md->anchor();
385 QCString ref;
386
387 // <keyword name="foo" id="MyApplication::foo" ref="doc.html#foo"/>
388 ref = makeRef(cfname, anchor);
389 QCString id = level1+"::"+level2;
390 writeIndent(p->index,3);
391 p->index << "<keyword name=\"" << convertToXML(level2 + argStr) << "\""
392 " id=\"" << convertToXML(id + "_" + anchor) << "\""
393 " ref=\"" << convertToXML(ref) << "\"/>\n";
394 }
395 else if (context) // container
396 {
397 // <keyword name="Foo" id="Foo" ref="doc.html#Foo"/>
398 QCString contRef = context->getOutputFileBase();
399 QCString level1 = !word.isEmpty() ? word : context->name();
400 QCString ref = makeRef(contRef,sectionAnchor);
401 writeIndent(p->index,3);
402 p->index << "<keyword name=\"" << convertToXML(level1) << "\""
403 << " id=\"" << convertToXML(level1 +"_" + sectionAnchor) << "\""
404 << " ref=\"" << convertToXML(ref) << "\"/>\n";
405 }
406}
407
408void Qhp::addFile(const QCString & fileName)
409{
410 p->files.insert(("<file>" + convertToXML(fileName) + "</file>").str());
411}
412
413void Qhp::addIndexFile(const QCString & fileName)
414{
415 addFile(fileName);
416}
417
418void Qhp::addImageFile(const QCString &fileName)
419{
420 addFile(fileName);
421}
422
423void Qhp::addStyleSheetFile(const QCString &fileName)
424{
425 addFile(fileName);
426}
427
429{
430 QCString const & qchFile = Config_getString(QCH_FILE);
431 if (!qchFile.isEmpty())
432 {
433 return qchFile;
434 }
435
436 QCString const & projectName = Config_getString(PROJECT_NAME);
437 QCString const & versionText = Config_getString(PROJECT_NUMBER);
438
439 return QCString("../qch/")
440 + (projectName.isEmpty() ? QCString("index") : projectName)
441 + (versionText.isEmpty() ? QCString("") : QCString("-") + versionText)
442 + QCString(".qch");
443}
@ Qhp
Definition debug.h:43
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:135
The common base class of all entity definitions found in the sources.
Definition definition.h:76
virtual QCString anchor() const =0
virtual QCString getOutputFileBase() const =0
virtual const QCString & name() const =0
static QCString htmlFileExtension
Definition doxygen.h:122
A model of a class/file/namespace member symbol.
Definition memberdef.h:48
virtual QCString argsString() const =0
This is an alternative implementation of QCString.
Definition qcstring.h:101
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:226
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:578
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:150
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:159
QhpSectionTree sectionTree
Definition qhp.cpp:158
TextStream doc
Definition qhp.cpp:155
TextStream index
Definition qhp.cpp:156
StringSet files
Definition qhp.cpp:157
std::ofstream docFile
Definition qhp.cpp:154
void addImageFile(const QCString &name)
Definition qhp.cpp:418
Qhp()
Definition qhp.cpp:196
void incContentsDepth()
Definition qhp.cpp:304
void addIndexFile(const QCString &name)
Definition qhp.cpp:413
void addStyleSheetFile(const QCString &name)
Definition qhp.cpp:423
static const QCString qhpFileName
Definition qhp.h:47
void decContentsDepth()
Definition qhp.cpp:309
void initialize()
Definition qhp.cpp:199
void addFile(const QCString &)
Definition qhp.cpp:408
void finalize()
Definition qhp.cpp:272
static QCString getQchFileName()
Definition qhp.cpp:428
void addContentsItem(bool isDir, const QCString &name, const QCString &ref, const QCString &file, const QCString &anchor, bool separateIndex, bool addToNavIndex, const Definition *def)
Definition qhp.cpp:314
std::unique_ptr< Private > p
Definition qhp.h:53
~Qhp()
void addIndexItem(const Definition *context, const MemberDef *md, const QCString &sectionAnchor, const QCString &title)
Definition qhp.cpp:369
void addSection(const QCString &title, const QCString &ref)
Definition qhp.cpp:123
Node m_root
Definition qhp.cpp:65
void writeToc(TextStream &t) const
Definition qhp.cpp:141
void incLevel()
Definition qhp.cpp:127
void traverse(const Node &root, TextStream &t, int indent) const
Definition qhp.cpp:68
Node * m_current
Definition qhp.cpp:66
void decLevel()
Definition qhp.cpp:133
Text streaming class that buffers data.
Definition textstream.h:36
void setStream(std::ostream *s)
Sets or changes the std::ostream to write to.
Definition textstream.h:67
void flush()
Flushes the buffer.
Definition textstream.h:209
#define Config_getString(name)
Definition config.h:32
std::set< std::string > StringSet
Definition containers.h:31
std::vector< std::string > StringVector
Definition containers.h:33
Translator * theTranslator
Definition language.cpp:71
#define term(fmt,...)
Definition message.h:94
std::ofstream openOutputStream(const QCString &name, bool append=false)
Definition portable.cpp:665
Portable versions of functions that are platform dependent.
const char * qPrint(const char *s)
Definition qcstring.h:672
static QCString makeRef(const QCString &withoutExtension, const QCString &anchor)
Definition qhp.cpp:187
static std::once_flag g_blankWritten
Definition qhp.cpp:34
static QCString makeFileName(const QCString &withoutExtension)
Definition qhp.cpp:170
static void writeIndent(TextStream &t, int indent)
Definition qhp.cpp:37
static QCString getFullProjectName()
Definition qhp.cpp:161
std::vector< std::unique_ptr< Node > > children
Definition qhp.cpp:62
QCString title
Definition qhp.cpp:60
Node(Node *parent_, const QCString &title_, const QCString &ref_)
Definition qhp.cpp:56
Node(Node *parent_)
Definition qhp.cpp:54
std::string_view word
Definition util.cpp:980
QCString convertToXML(const QCString &s, bool keepEntities)
Definition util.cpp:4266
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
Definition util.cpp:6946
void addHtmlExtensionIfMissing(QCString &fName)
Definition util.cpp:5243
A bunch of utility functions.