source: trunk/tools/qdoc3/pagegenerator.cpp@ 865

Last change on this file since 865 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 9.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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 "pagegenerator.h"
50#include "tree.h"
51
52QT_BEGIN_NAMESPACE
53
54/*!
55 Nothing to do in the constructor.
56 */
57PageGenerator::PageGenerator()
58{
59 // nothing.
60}
61
62/*!
63 The destructor
64 */
65PageGenerator::~PageGenerator()
66{
67 while (!outStreamStack.isEmpty())
68 endSubPage();
69}
70
71static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
72static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)");
73static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)");
74static QRegExp spanTag("</@(?:comment|preprocessor|string|char)>");
75static QRegExp unknownTag("</?@[^>]*>");
76
77bool PageGenerator::parseArg(const QString& src,
78 const QString& tag,
79 int* pos,
80 int n,
81 QStringRef* contents,
82 QStringRef* par1,
83 bool debug)
84{
85#define SKIP_CHAR(c) \
86 if (debug) \
87 qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \
88 if (i >= n || src[i] != c) { \
89 if (debug) \
90 qDebug() << " char '" << c << "' not found"; \
91 return false; \
92 } \
93 ++i;
94
95
96#define SKIP_SPACE \
97 while (i < n && src[i] == ' ') \
98 ++i;
99
100 int i = *pos;
101 int j = i;
102
103 // assume "<@" has been parsed outside
104 //SKIP_CHAR('<');
105 //SKIP_CHAR('@');
106
107 if (tag != QStringRef(&src, i, tag.length())) {
108 if (0 && debug)
109 qDebug() << "tag " << tag << " not found at " << i;
110 return false;
111 }
112
113 if (debug)
114 qDebug() << "haystack:" << src << "needle:" << tag << "i:" <<i;
115
116 // skip tag
117 i += tag.length();
118
119 // parse stuff like: linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
120 if (par1) {
121 SKIP_SPACE;
122 // read parameter name
123 j = i;
124 while (i < n && src[i].isLetter())
125 ++i;
126 if (src[i] == '=') {
127 if (debug)
128 qDebug() << "read parameter" << QString(src.data() + j, i - j);
129 SKIP_CHAR('=');
130 SKIP_CHAR('"');
131 // skip parameter name
132 j = i;
133 while (i < n && src[i] != '"')
134 ++i;
135 *par1 = QStringRef(&src, j, i - j);
136 SKIP_CHAR('"');
137 SKIP_SPACE;
138 } else {
139 if (debug)
140 qDebug() << "no optional parameter found";
141 }
142 }
143 SKIP_SPACE;
144 SKIP_CHAR('>');
145
146 // find contents up to closing "</@tag>
147 j = i;
148 for (; true; ++i) {
149 if (i + 4 + tag.length() > n)
150 return false;
151 if (src[i] != '<')
152 continue;
153 if (src[i + 1] != '/')
154 continue;
155 if (src[i + 2] != '@')
156 continue;
157 if (tag != QStringRef(&src, i + 3, tag.length()))
158 continue;
159 if (src[i + 3 + tag.length()] != '>')
160 continue;
161 break;
162 }
163
164 *contents = QStringRef(&src, j, i - j);
165
166 i += tag.length() + 4;
167
168 *pos = i;
169 if (debug)
170 qDebug() << " tag " << tag << " found: pos now: " << i;
171 return true;
172#undef SKIP_CHAR
173}
174
175/*!
176 This function is recursive.
177 */
178void PageGenerator::generateTree(const Tree *tree, CodeMarker *marker)
179{
180 generateInnerNode(tree->root(), marker);
181}
182
183QString PageGenerator::fileBase(const Node *node) const
184{
185 if (node->relates())
186 node = node->relates();
187 else if (!node->isInnerNode())
188 node = node->parent();
189#ifdef QDOC_QML
190 if (node->subType() == Node::QmlPropertyGroup) {
191 node = node->parent();
192 }
193#endif
194
195 QString base = node->doc().baseName();
196 if (!base.isEmpty())
197 return base;
198
199 const Node *p = node;
200
201 forever {
202 const Node *pp = p->parent();
203 base.prepend(p->name());
204#ifdef QDOC_QML
205 /*
206 To avoid file name conflicts in the html directory,
207 we prepend "qml-" to the file name of QML element doc
208 files.
209 */
210 if ((p->subType() == Node::QmlClass) ||
211 (p->subType() == Node::QmlBasicType)) {
212 if (!base.startsWith(QLatin1String("QML:")))
213 base.prepend("qml-");
214 }
215#endif
216 if (!pp || pp->name().isEmpty() || pp->type() == Node::Fake)
217 break;
218 base.prepend(QLatin1Char('-'));
219 p = pp;
220 }
221
222 if (node->type() == Node::Fake) {
223#ifdef QDOC2_COMPAT
224 if (base.endsWith(".html"))
225 base.truncate(base.length() - 5);
226#endif
227 }
228
229 // the code below is effectively equivalent to:
230 // base.replace(QRegExp("[^A-Za-z0-9]+"), " ");
231 // base = base.trimmed();
232 // base.replace(" ", "-");
233 // base = base.toLower();
234 // as this function accounted for ~8% of total running time
235 // we optimize a bit...
236
237 QString res;
238 // +5 prevents realloc in fileName() below
239 res.reserve(base.size() + 5);
240 bool begun = false;
241 for (int i = 0; i != base.size(); ++i) {
242 QChar c = base.at(i);
243 uint u = c.unicode();
244 if (u >= 'A' && u <= 'Z')
245 u -= 'A' - 'a';
246 if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9')) {
247 res += QLatin1Char(u);
248 begun = true;
249 }
250 else if (begun) {
251 res += QLatin1Char('-');
252 begun = false;
253 }
254 }
255 while (res.endsWith(QLatin1Char('-')))
256 res.chop(1);
257 return res;
258}
259
260QString PageGenerator::fileName(const Node *node) const
261{
262 if (!node->url().isEmpty())
263 return node->url();
264
265 QString name = fileBase(node);
266 name += QLatin1Char('.');
267 name += fileExtension(node);
268 return name;
269}
270
271QString PageGenerator::outFileName()
272{
273 return QFileInfo(static_cast<QFile *>(out().device())->fileName()).fileName();
274}
275
276void PageGenerator::beginSubPage(const Location& location,
277 const QString& fileName)
278{
279 QFile *outFile = new QFile(outputDir() + "/" + fileName);
280 if (!outFile->open(QFile::WriteOnly))
281 location.fatal(tr("Cannot open output file '%1'")
282 .arg(outFile->fileName()));
283 QTextStream *out = new QTextStream(outFile);
284 out->setCodec(outputCodec);
285 outStreamStack.push(out);
286}
287
288void PageGenerator::endSubPage()
289{
290 outStreamStack.top()->flush();
291 delete outStreamStack.top()->device();
292 delete outStreamStack.pop();
293}
294
295QTextStream &PageGenerator::out()
296{
297 return *outStreamStack.top();
298}
299
300/*!
301 Recursive writing of html files from the root \a node.
302 */
303void
304PageGenerator::generateInnerNode(const InnerNode* node, CodeMarker* marker)
305{
306 if (!node->url().isNull())
307 return;
308
309 if (node->type() == Node::Fake) {
310 const FakeNode *fakeNode = static_cast<const FakeNode *>(node);
311 if (fakeNode->subType() == Node::ExternalPage)
312 return;
313 if (fakeNode->subType() == Node::Image)
314 return;
315 if (fakeNode->subType() == Node::QmlPropertyGroup)
316 return;
317 if (fakeNode->subType() == Node::Page) {
318 if (node->count() > 0)
319 qDebug("PAGE %s HAS CHILDREN", qPrintable(fakeNode->title()));
320 }
321 }
322
323 if (node->parent() != 0) {
324 beginSubPage(node->location(), fileName(node));
325 if (node->type() == Node::Namespace || node->type() == Node::Class) {
326 generateClassLikeNode(node, marker);
327 }
328 else if (node->type() == Node::Fake) {
329 generateFakeNode(static_cast<const FakeNode *>(node), marker);
330 }
331 endSubPage();
332 }
333
334 NodeList::ConstIterator c = node->childNodes().begin();
335 while (c != node->childNodes().end()) {
336 if ((*c)->isInnerNode() && (*c)->access() != Node::Private)
337 generateInnerNode((const InnerNode *) *c, marker);
338 ++c;
339 }
340}
341
342QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.