source: trunk/tools/assistant/lib/qhelpprojectdata.cpp@ 561

Last change on this file since 561 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 13.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 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 Qt Assistant 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#include "qhelpprojectdata_p.h"
43
44#include <QtCore/QDir>
45#include <QtCore/QFileInfo>
46#include <QtCore/QStack>
47#include <QtCore/QMap>
48#include <QtCore/QRegExp>
49#include <QtCore/QVariant>
50#include <QtXml/QXmlStreamReader>
51
52QT_BEGIN_NAMESPACE
53
54class QHelpProjectDataPrivate : public QXmlStreamReader
55{
56public:
57 void readData(const QByteArray &contents);
58
59 QString virtualFolder;
60 QString namespaceName;
61 QString rootPath;
62
63 QStringList fileList;
64 QList<QHelpDataCustomFilter> customFilterList;
65 QList<QHelpDataFilterSection> filterSectionList;
66 QMap<QString, QVariant> metaData;
67
68 QString errorMsg;
69
70private:
71 void readProject();
72 void readCustomFilter();
73 void readFilterSection();
74 void readTOC();
75 void readKeywords();
76 void readFiles();
77 void raiseUnknownTokenError();
78 void addMatchingFiles(const QString &pattern);
79
80 QMap<QString, QStringList> dirEntriesCache;
81};
82
83void QHelpProjectDataPrivate::raiseUnknownTokenError()
84{
85 raiseError(QObject::tr("Unknown token."));
86}
87
88void QHelpProjectDataPrivate::readData(const QByteArray &contents)
89{
90 addData(contents);
91 while (!atEnd()) {
92 readNext();
93 if (isStartElement()) {
94 if (name() == QLatin1String("QtHelpProject")
95 && attributes().value(QLatin1String("version")) == QLatin1String("1.0"))
96 readProject();
97 else
98 raiseError(QObject::tr("Unknown token. Expected \"QtHelpProject\"!"));
99 }
100 }
101
102 if (hasError()) {
103 raiseError(QObject::tr("Error in line %1: %2").arg(lineNumber())
104 .arg(errorString()));
105 }
106}
107
108void QHelpProjectDataPrivate::readProject()
109{
110 while (!atEnd()) {
111 readNext();
112 if (isStartElement()) {
113 if (name() == QLatin1String("virtualFolder")) {
114 virtualFolder = readElementText();
115 if (virtualFolder.contains(QLatin1String("/")))
116 raiseError(QObject::tr("A virtual folder must not contain a \'/\' character!"));
117 } else if (name() == QLatin1String("namespace")) {
118 namespaceName = readElementText();
119 if (namespaceName.contains(QLatin1String("/")))
120 raiseError(QObject::tr("A namespace must not contain a \'/\' character!"));
121 } else if (name() == QLatin1String("customFilter")) {
122 readCustomFilter();
123 } else if (name() == QLatin1String("filterSection")) {
124 readFilterSection();
125 } else if (name() == QLatin1String("metaData")) {
126 QString n = attributes().value(QLatin1String("name")).toString();
127 if (!metaData.contains(n))
128 metaData[n] = attributes().value(QLatin1String("value")).toString();
129 else
130 metaData.insert(n, attributes().value(QLatin1String("value")).toString());
131 } else {
132 raiseUnknownTokenError();
133 }
134 } else if (isEndElement() && name() == QLatin1String("QtHelpProject")) {
135 if (namespaceName.isEmpty())
136 raiseError(QObject::tr("Missing namespace in QtHelpProject."));
137 else if (virtualFolder.isEmpty())
138 raiseError(QObject::tr("Missing virtual folder in QtHelpProject"));
139 break;
140 }
141 }
142}
143
144void QHelpProjectDataPrivate::readCustomFilter()
145{
146 QHelpDataCustomFilter filter;
147 filter.name = attributes().value(QLatin1String("name")).toString();
148 while (!atEnd()) {
149 readNext();
150 if (isStartElement()) {
151 if (name() == QLatin1String("filterAttribute"))
152 filter.filterAttributes.append(readElementText());
153 else
154 raiseUnknownTokenError();
155 } else if (isEndElement() && name() == QLatin1String("customFilter")) {
156 break;
157 }
158 }
159 customFilterList.append(filter);
160}
161
162void QHelpProjectDataPrivate::readFilterSection()
163{
164 filterSectionList.append(QHelpDataFilterSection());
165 while (!atEnd()) {
166 readNext();
167 if (isStartElement()) {
168 if (name() == QLatin1String("filterAttribute"))
169 filterSectionList.last().addFilterAttribute(readElementText());
170 else if (name() == QLatin1String("toc"))
171 readTOC();
172 else if (name() == QLatin1String("keywords"))
173 readKeywords();
174 else if (name() == QLatin1String("files"))
175 readFiles();
176 else
177 raiseUnknownTokenError();
178 } else if (isEndElement() && name() == QLatin1String("filterSection")) {
179 break;
180 }
181 }
182}
183
184void QHelpProjectDataPrivate::readTOC()
185{
186 QStack<QHelpDataContentItem*> contentStack;
187 QHelpDataContentItem *itm = 0;
188 while (!atEnd()) {
189 readNext();
190 if (isStartElement()) {
191 if (name() == QLatin1String("section")) {
192 QString title = attributes().value(QLatin1String("title")).toString();
193 QString ref = attributes().value(QLatin1String("ref")).toString();
194 if (contentStack.isEmpty()) {
195 itm = new QHelpDataContentItem(0, title, ref);
196 filterSectionList.last().addContent(itm);
197 } else {
198 itm = new QHelpDataContentItem(contentStack.top(), title, ref);
199 }
200 contentStack.push(itm);
201 } else {
202 raiseUnknownTokenError();
203 }
204 } else if (isEndElement()) {
205 if (name() == QLatin1String("section")) {
206 contentStack.pop();
207 continue;
208 } else if (name() == QLatin1String("toc") && contentStack.isEmpty()) {
209 break;
210 } else {
211 raiseUnknownTokenError();
212 }
213 }
214 }
215}
216
217void QHelpProjectDataPrivate::readKeywords()
218{
219 while (!atEnd()) {
220 readNext();
221 if (isStartElement()) {
222 if (name() == QLatin1String("keyword")) {
223 if (attributes().value(QLatin1String("ref")).toString().isEmpty()
224 || (attributes().value(QLatin1String("name")).toString().isEmpty()
225 && attributes().value(QLatin1String("id")).toString().isEmpty()))
226 raiseError(QObject::tr("Missing attribute in keyword at line %1.")
227 .arg(lineNumber()));
228 filterSectionList.last().addIndex(
229 QHelpDataIndexItem(attributes().value(QLatin1String("name")).toString(),
230 attributes().value(QLatin1String("id")).toString(),
231 attributes().value(QLatin1String("ref")).toString()));
232 } else {
233 raiseUnknownTokenError();
234 }
235 } else if (isEndElement()) {
236 if (name() == QLatin1String("keyword"))
237 continue;
238 else if (name() == QLatin1String("keywords"))
239 break;
240 else
241 raiseUnknownTokenError();
242 }
243 }
244}
245
246void QHelpProjectDataPrivate::readFiles()
247{
248 while (!atEnd()) {
249 readNext();
250 if (isStartElement()) {
251 if (name() == QLatin1String("file"))
252 addMatchingFiles(readElementText());
253 else
254 raiseUnknownTokenError();
255 } else if (isEndElement()) {
256 if (name() == QLatin1String("file"))
257 continue;
258 else if (name() == QLatin1String("files"))
259 break;
260 else
261 raiseUnknownTokenError();
262 }
263 }
264}
265
266// Expand file pattern and add matches into list. If the pattern does not match
267// any files, insert the pattern itself so the QHelpGenerator will emit a
268// meaningful warning later.
269void QHelpProjectDataPrivate::addMatchingFiles(const QString &pattern)
270{
271 // The pattern matching is expensive, so we skip it if no
272 // wildcard symbols occur in the string.
273 if (!pattern.contains('?') && !pattern.contains('*')
274 && !pattern.contains('[') && !pattern.contains(']')) {
275 filterSectionList.last().addFile(pattern);
276 return;
277 }
278
279 QFileInfo fileInfo(rootPath + '/' + pattern);
280 const QDir &dir = fileInfo.dir();
281 const QString &path = dir.canonicalPath();
282
283 // QDir::entryList() is expensive, so we cache the results.
284 QMap<QString, QStringList>::ConstIterator it = dirEntriesCache.find(path);
285 const QStringList &entries = it != dirEntriesCache.constEnd() ?
286 it.value() : dir.entryList(QDir::Files);
287 if (it == dirEntriesCache.constEnd())
288 dirEntriesCache.insert(path, entries);
289
290 bool matchFound = false;
291#ifdef Q_OS_WIN
292 Qt::CaseSensitivity cs = Qt::CaseInsensitive;
293#else
294 Qt::CaseSensitivity cs = Qt::CaseSensitive;
295#endif
296 QRegExp regExp(fileInfo.fileName(), cs, QRegExp::Wildcard);
297 foreach (const QString &file, entries) {
298 if (regExp.exactMatch(file)) {
299 matchFound = true;
300 filterSectionList.last().
301 addFile(QFileInfo(pattern).dir().path() + '/' + file);
302 }
303 }
304 if (!matchFound)
305 filterSectionList.last().addFile(pattern);
306}
307
308/*!
309 \internal
310 \class QHelpProjectData
311 \since 4.4
312 \brief The QHelpProjectData class stores all information found
313 in a Qt help project file.
314
315 The structure is filled with data by calling readData(). The
316 specified file has to have the Qt help project file format in
317 order to be read successfully. Possible reading errors can be
318 retrieved by calling errorMessage().
319*/
320
321/*!
322 Constructs a Qt help project data structure.
323*/
324QHelpProjectData::QHelpProjectData()
325{
326 d = new QHelpProjectDataPrivate;
327}
328
329/*!
330 Destroys the help project data.
331*/
332QHelpProjectData::~QHelpProjectData()
333{
334 delete d;
335}
336
337/*!
338 Reads the file \a fileName and stores the help data. The file has to
339 have the Qt help project file format. Returns true if the file
340 was successfully read, otherwise false.
341
342 \sa errorMessage()
343*/
344bool QHelpProjectData::readData(const QString &fileName)
345{
346 d->rootPath = QFileInfo(fileName).absolutePath();
347 QFile file(fileName);
348 if (!file.open(QIODevice::ReadOnly)) {
349 d->errorMsg = QObject::tr("The input file %1 could not be opened!")
350 .arg(fileName);
351 return false;
352 }
353
354 d->readData(file.readAll());
355 return !d->hasError();
356}
357
358/*!
359 Returns an error message if the reading of the Qt help project
360 file failed. Otherwise, an empty QString is returned.
361
362 \sa readData()
363*/
364QString QHelpProjectData::errorMessage() const
365{
366 if (d->hasError())
367 return d->errorString();
368 return d->errorMsg;
369}
370
371/*!
372 \internal
373*/
374QString QHelpProjectData::namespaceName() const
375{
376 return d->namespaceName;
377}
378
379/*!
380 \internal
381*/
382QString QHelpProjectData::virtualFolder() const
383{
384 return d->virtualFolder;
385}
386
387/*!
388 \internal
389*/
390QList<QHelpDataCustomFilter> QHelpProjectData::customFilters() const
391{
392 return d->customFilterList;
393}
394
395/*!
396 \internal
397*/
398QList<QHelpDataFilterSection> QHelpProjectData::filterSections() const
399{
400 return d->filterSectionList;
401}
402
403/*!
404 \internal
405*/
406QMap<QString, QVariant> QHelpProjectData::metaData() const
407{
408 return d->metaData;
409}
410
411/*!
412 \internal
413*/
414QString QHelpProjectData::rootPath() const
415{
416 return d->rootPath;
417}
418
419QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.