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 | |
53 | QT_BEGIN_NAMESPACE |
54 | |
55 | /*! |
56 | Nothing to do in the constructor. |
57 | */ |
58 | PageGenerator::PageGenerator() |
59 | : outputCodec(0) |
60 | { |
61 | // nothing. |
62 | } |
63 | |
64 | /*! |
65 | The destructor |
66 | */ |
67 | PageGenerator::~PageGenerator() |
68 | { |
69 | while (!outStreamStack.isEmpty()) |
70 | endSubPage(); |
71 | } |
72 | |
73 | bool 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 | */ |
174 | void PageGenerator::generateTree(const Tree *tree) |
175 | { |
176 | generateInnerNode(tree->root()); |
177 | } |
178 | |
179 | QString 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 >= |
241 | u -= |
242 | if ((u >= |
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 | */ |
261 | QString 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 | */ |
275 | QString 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 | */ |
285 | void 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 | */ |
303 | void 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 | */ |
315 | QTextStream &PageGenerator::out() |
316 | { |
317 | return *outStreamStack.top(); |
318 | } |
319 | |
320 | /*! |
321 | Recursive writing of HTML files from the root \a node. |
322 | */ |
323 | void |
324 | PageGenerator::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 | |
367 | QT_END_NAMESPACE |
368 |
Warning: That file was not part of the compilation database. It may have many parsing errors.