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 pagegenerator.cpp
44*/
45
46#include <qfile.h>
47#include <qfileinfo.h>
48#include <qdebug.h>
49#include "codemarker.h"
50#include "pagegenerator.h"
51#include "tree.h"
52
53QT_BEGIN_NAMESPACE
54
55/*!
56 Nothing to do in the constructor.
57 */
58PageGenerator::PageGenerator()
59 : outputCodec(0)
60{
61 // nothing.
62}
63
64/*!
65 The destructor
66 */
67PageGenerator::~PageGenerator()
68{
69 while (!outStreamStack.isEmpty())
70 endSubPage();
71}
72
73bool PageGenerator::parseArg(const QString& src,
74 const QString& tag,
75 int* pos,
76 int n,
77 QStringRef* contents,
78 QStringRef* par1,
79 bool debug)
80{
81#define SKIP_CHAR(c) \
82 if (debug) \
83 qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \
84 if (i >= n || src[i] != c) { \
85 if (debug) \
86 qDebug() << " char '" << c << "' not found"; \
87 return false; \
88 } \
89 ++i;
90
91
92#define SKIP_SPACE \
93 while (i < n && src[i] == ' ') \
94 ++i;
95
96 int i = *pos;
97 int j = i;
98
99 // assume "<@" has been parsed outside
100 //SKIP_CHAR('<');
101 //SKIP_CHAR('@');
102
103 if (tag != QStringRef(&src, i, tag.length())) {
104 if (0 && debug)
105 qDebug() << "tag " << tag << " not found at " << i;
106 return false;
107 }
108
109 if (debug)
110 qDebug() << "haystack:" << src << "needle:" << tag << "i:" <<i;
111
112 // skip tag
113 i += tag.length();
114
115 // parse stuff like: linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
116 if (par1) {
117 SKIP_SPACE;
118 // read parameter name
119 j = i;
120 while (i < n && src[i].isLetter())
121 ++i;
122 if (src[i] == '=') {
123 if (debug)
124 qDebug() << "read parameter" << QString(src.data() + j, i - j);
125 SKIP_CHAR('=');
126 SKIP_CHAR('"');
127 // skip parameter name
128 j = i;
129 while (i < n && src[i] != '"')
130 ++i;
131 *par1 = QStringRef(&src, j, i - j);
132 SKIP_CHAR('"');
133 SKIP_SPACE;
134 } else {
135 if (debug)
136 qDebug() << "no optional parameter found";
137 }
138 }
139 SKIP_SPACE;
140 SKIP_CHAR('>');
141
142 // find contents up to closing "</@tag>
143 j = i;
144 for (; true; ++i) {
145 if (i + 4 + tag.length() > n)
146 return false;
147 if (src[i] != '<')
148 continue;
149 if (src[i + 1] != '/')
150 continue;
151 if (src[i + 2] != '@')
152 continue;
153 if (tag != QStringRef(&src, i + 3, tag.length()))
154 continue;
155 if (src[i + 3 + tag.length()] != '>')
156 continue;
157 break;
158 }
159
160 *contents = QStringRef(&src, j, i - j);
161
162 i += tag.length() + 4;
163
164 *pos = i;
165 if (debug)
166 qDebug() << " tag " << tag << " found: pos now: " << i;
167 return true;
168#undef SKIP_CHAR
169}
170
171/*!
172 This function is recursive.
173 */
174void PageGenerator::generateTree(const Tree *tree)
175{
176 generateInnerNode(tree->root());
177}
178
179QString PageGenerator::fileBase(const Node *node) const
180{
181 if (node->relates())
182 node = node->relates();
183 else if (!node->isInnerNode())
184 node = node->parent();
185#ifdef QDOC_QML
186 if (node->subType() == Node::QmlPropertyGroup) {
187 node = node->parent();
188 }
189#endif
190
191 QString base = node->doc().baseName();
192 if (!base.isEmpty())
193 return base;
194
195 const Node *p = node;
196
197 forever {
198 const Node *pp = p->parent();
199 base.prepend(p->name());
200#ifdef QDOC_QML
201 /*
202 To avoid file name conflicts in the html directory,
203 we prepend a prefix (by default, "qml-") to the file name of QML
204 element doc files.
205 */
206 if ((p->subType() == Node::QmlClass) ||
207 (p->subType() == Node::QmlBasicType)) {
208 if (!base.startsWith(QLatin1String("QML:")))
209 base.prepend(outputPrefix(QLatin1String("QML")));
210 }
211#endif
212 if (!pp || pp->name().isEmpty() || pp->type() == Node::Fake)
213 break;
214 base.prepend(QLatin1Char('-'));
215 p = pp;
216 }
217
218 if (node->type() == Node::Fake) {
219#ifdef QDOC2_COMPAT
220 if (base.endsWith(".html"))
221 base.truncate(base.length() - 5);
222#endif
223 }
224
225 // the code below is effectively equivalent to:
226 // base.replace(QRegExp("[^A-Za-z0-9]+"), " ");
227 // base = base.trimmed();
228 // base.replace(" ", "-");
229 // base = base.toLower();
230 // as this function accounted for ~8% of total running time
231 // we optimize a bit...
232
233 QString res;
234 // +5 prevents realloc in fileName() below
235 res.reserve(base.size() + 5);
236 bool begun = false;
237 for (int i = 0; i != base.size(); ++i) {
238 QChar c = base.at(i);
239 uint u = c.unicode();
240 if (u >= 'A' && u <= 'Z')
241 u -= 'A' - 'a';
242 if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9')) {
243 res += QLatin1Char(u);
244 begun = true;
245 }
246 else if (begun) {
247 res += QLatin1Char('-');
248 begun = false;
249 }
250 }
251 while (res.endsWith(QLatin1Char('-')))
252 res.chop(1);
253 return res;
254}
255
256/*!
257 If the \a node has a URL, return the URL as the file name.
258 Otherwise, construct the file name from the fileBase() and
259 the fileExtension(), and return the constructed name.
260 */
261QString PageGenerator::fileName(const Node* node) const
262{
263 if (!node->url().isEmpty())
264 return node->url();
265
266 QString name = fileBase(node);
267 name += QLatin1Char('.');
268 name += fileExtension(node);
269 return name;
270}
271
272/*!
273 Return the current output file name.
274 */
275QString PageGenerator::outFileName()
276{
277 return QFileInfo(static_cast<QFile*>(out().device())->fileName()).fileName();
278}
279
280/*!
281 Creates the file named \a fileName in the output directory.
282 Attaches a QTextStream to the created file, which is written
283 to all over the place using out().
284 */
285void PageGenerator::beginSubPage(const Location& location,
286 const QString& fileName)
287{
288 QFile* outFile = new QFile(outputDir() + "/" + fileName);
289 if (!outFile->open(QFile::WriteOnly))
290 location.fatal(tr("Cannot open output file '%1'").arg(outFile->fileName()));
291 QTextStream* out = new QTextStream(outFile);
292
293 if (outputCodec)
294 out->setCodec(outputCodec);
295 outStreamStack.push(out);
296}
297
298/*!
299 Flush the text stream associated with the subpage, and
300 then pop it off the text stream stack and delete it.
301 This terminates output of the subpage.
302 */
303void PageGenerator::endSubPage()
304{
305 outStreamStack.top()->flush();
306 delete outStreamStack.top()->device();
307 delete outStreamStack.pop();
308}
309
310/*!
311 Used for writing to the current output stream. Returns a
312 reference to the crrent output stream, which is then used
313 with the \c {<<} operator for writing.
314 */
315QTextStream &PageGenerator::out()
316{
317 return *outStreamStack.top();
318}
319
320/*!
321 Recursive writing of HTML files from the root \a node.
322 */
323void
324PageGenerator::generateInnerNode(const InnerNode* node)
325{
326 if (!node->url().isNull())
327 return;
328
329 if (node->type() == Node::Fake) {
330 const FakeNode *fakeNode = static_cast<const FakeNode *>(node);
331 if (fakeNode->subType() == Node::ExternalPage)
332 return;
333 if (fakeNode->subType() == Node::Image)
334 return;
335 if (fakeNode->subType() == Node::QmlPropertyGroup)
336 return;
337 if (fakeNode->subType() == Node::Page) {
338 if (node->count() > 0)
339 qDebug("PAGE %s HAS CHILDREN", qPrintable(fakeNode->title()));
340 }
341 }
342
343 /*
344 Obtain a code marker for the source file.
345 */
346 CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath());
347
348 if (node->parent() != 0) {
349 beginSubPage(node->location(), fileName(node));
350 if (node->type() == Node::Namespace || node->type() == Node::Class) {
351 generateClassLikeNode(node, marker);
352 }
353 else if (node->type() == Node::Fake) {
354 generateFakeNode(static_cast<const FakeNode *>(node), marker);
355 }
356 endSubPage();
357 }
358
359 NodeList::ConstIterator c = node->childNodes().begin();
360 while (c != node->childNodes().end()) {
361 if ((*c)->isInnerNode() && (*c)->access() != Node::Private)
362 generateInnerNode((const InnerNode *) *c);
363 ++c;
364 }
365}
366
367QT_END_NAMESPACE
368

Warning: That file was not part of the compilation database. It may have many parsing errors.