6#include <QtCore/QCoreApplication>
8#include <QtCore/QFileInfo>
9#include <QtCore/QStack>
11#include <QtCore/QRegularExpression>
12#include <QtCore/QTextStream>
14#include <QtCore/QVariant>
15#include <QtCore/QXmlStreamReader>
37 void readCustomFilter();
38 void readFilterSection();
42 void skipUnknownToken();
43 void addMatchingFiles(
const QString &pattern);
44 bool hasValidSyntax(
const QString &nameSpace,
const QString &vFolder)
const;
46 QMap<QString, QStringList> dirEntriesCache;
51 const QString message = QCoreApplication::translate(
"QHelpProject",
52 "Skipping unknown token <%1> in file \"%2\".")
53 .arg(name()).arg(fileName) + QLatin1Char(
'\n');
54 fputs(qPrintable(message), stdout);
64 if (isStartElement()) {
65 if (name() == QLatin1String(
"QtHelpProject")
66 && attributes().value(QLatin1String(
"version"))
67 == QLatin1String(
"1.0")) {
70 raiseError(QCoreApplication::translate(
"QHelpProject",
71 "Unknown token. Expected \"QtHelpProject\"."));
77 raiseError(QCoreApplication::translate(
"QHelpProject",
78 "Error in line %1: %2").arg(lineNumber())
87 if (isStartElement()) {
88 if (name() == QLatin1String(
"virtualFolder")) {
89 virtualFolder = readElementText();
90 if (!hasValidSyntax(QLatin1String(
"test"), virtualFolder))
91 raiseError(QCoreApplication::translate(
"QHelpProject",
92 "Virtual folder has invalid syntax in file: \"%1\"").arg(fileName));
93 }
else if (name() == QLatin1String(
"namespace")) {
94 namespaceName = readElementText();
95 if (!hasValidSyntax(namespaceName, QLatin1String(
"test")))
96 raiseError(QCoreApplication::translate(
"QHelpProject",
97 "Namespace \"%1\" has invalid syntax in file: \"%2\"").arg(namespaceName, fileName));
98 }
else if (name() == QLatin1String(
"customFilter")) {
100 }
else if (name() == QLatin1String(
"filterSection")) {
102 }
else if (name() == QLatin1String(
"metaData")) {
103 QString n = attributes().value(QLatin1String(
"name")).toString();
104 if (!metaData.contains(n))
106 = attributes().value(QLatin1String(
"value")).toString();
108 metaData.insert(n, attributes().
109 value(QLatin1String(
"value")).toString());
113 }
else if (isEndElement() && name() == QLatin1String(
"QtHelpProject")) {
114 if (namespaceName.isEmpty())
115 raiseError(QCoreApplication::translate(
"QHelpProject",
116 "Missing namespace in QtHelpProject file: \"%1\"").arg(fileName));
117 else if (virtualFolder.isEmpty())
118 raiseError(QCoreApplication::translate(
"QHelpProject",
119 "Missing virtual folder in QtHelpProject file: \"%1\"").arg(fileName));
128 filter.name = attributes().value(QLatin1String(
"name")).toString();
131 if (isStartElement()) {
132 if (name() == QLatin1String(
"filterAttribute"))
133 filter.filterAttributes.append(readElementText());
136 }
else if (isEndElement() && name() == QLatin1String(
"customFilter")) {
140 customFilterList.append(filter);
145 filterSectionList.append(QHelpDataFilterSection());
148 if (isStartElement()) {
149 if (name() == QLatin1String(
"filterAttribute"))
150 filterSectionList.last().addFilterAttribute(readElementText());
151 else if (name() == QLatin1String(
"toc"))
153 else if (name() == QLatin1String(
"keywords"))
155 else if (name() == QLatin1String(
"files"))
159 }
else if (isEndElement() && name() == QLatin1String(
"filterSection")) {
167 QStack<QHelpDataContentItem*> contentStack;
168 QHelpDataContentItem *itm =
nullptr;
171 if (isStartElement()) {
172 if (name() == QLatin1String(
"section")) {
173 const QString &title = attributes().value(QLatin1String(
"title")).toString();
174 const QString &ref = attributes().value(QLatin1String(
"ref")).toString();
175 if (contentStack.isEmpty()) {
176 itm =
new QHelpDataContentItem(
nullptr, title, ref);
177 filterSectionList.last().addContent(itm);
179 itm =
new QHelpDataContentItem(contentStack.top(), title, ref);
181 contentStack.push(itm);
185 }
else if (isEndElement()) {
186 if (name() == QLatin1String(
"section")) {
189 }
else if (name() == QLatin1String(
"toc") && contentStack.isEmpty()) {
201 QTextStream str(&result);
202 str << QDir::toNativeSeparators(fileName) <<
':' << lineNumber
203 <<
": Missing attribute in <keyword";
205 str <<
" name=\"" << name <<
'"';
214 if (isStartElement()) {
215 if (name() == QLatin1String(
"keyword")) {
216 const QString &refAttribute = attributes().value(QStringLiteral(
"ref")).toString();
217 const QString &nameAttribute = attributes().value(QStringLiteral(
"name")).toString();
218 const QString &idAttribute = attributes().value(QStringLiteral(
"id")).toString();
219 if (refAttribute.isEmpty() || (nameAttribute.isEmpty() && idAttribute.isEmpty())) {
220 qWarning(
"%s", qPrintable(msgMissingAttribute(fileName, lineNumber(), nameAttribute)));
223 filterSectionList.last()
224 .addIndex(QHelpDataIndexItem(nameAttribute, idAttribute, refAttribute));
228 }
else if (isEndElement()) {
229 if (name() == QLatin1String(
"keyword"))
231 else if (name() == QLatin1String(
"keywords"))
243 if (isStartElement()) {
244 if (name() == QLatin1String(
"file"))
245 addMatchingFiles(readElementText());
248 }
else if (isEndElement()) {
249 if (name() == QLatin1String(
"file"))
251 else if (name() == QLatin1String(
"files"))
266 if (!pattern.contains(QLatin1Char(
'?')) && !pattern.contains(QLatin1Char(
'*'))
267 && !pattern.contains(QLatin1Char(
'[')) && !pattern.contains(QLatin1Char(
']'))) {
268 filterSectionList.last().addFile(pattern);
272 const QFileInfo fileInfo(rootPath + QLatin1Char(
'/') + pattern);
273 const QDir &dir = fileInfo.dir();
274 const QString &path = dir.canonicalPath();
277 const auto &it = dirEntriesCache.constFind(path);
278 const QStringList &entries = it != dirEntriesCache.cend() ?
279 it.value() : dir.entryList(QDir::Files);
280 if (it == dirEntriesCache.cend())
281 dirEntriesCache.insert(path, entries);
283 bool matchFound =
false;
285 auto cs = QRegularExpression::CaseInsensitiveOption;
287 auto cs = QRegularExpression::NoPatternOption;
289 const QRegularExpression regExp(QRegularExpression::wildcardToRegularExpression(fileInfo.fileName()), cs);
290 for (
const QString &file : entries) {
291 auto match = regExp.match(file);
292 if (match.hasMatch()) {
294 filterSectionList.last().
295 addFile(QFileInfo(pattern).dir().path() + QLatin1Char(
'/') + file);
299 filterSectionList.last().addFile(pattern);
303 const QString &vFolder)
const
305 const QLatin1Char slash(
'/');
306 if (nameSpace.contains(slash) || vFolder.contains(slash))
309 const QLatin1String scheme(
"qthelp");
310 url.setScheme(scheme);
311 const QString &canonicalNamespace = nameSpace.toLower();
312 url.setHost(canonicalNamespace);
313 url.setPath(slash + vFolder);
315 const QString expectedUrl(scheme + QLatin1String(
"://")
316 + canonicalNamespace + slash + vFolder);
317 return url.isValid() && url.toString() == expectedUrl;
321
322
323
324
325
326
327
328
329
330
331
334
335
342
343
350
351
352
353
354
355
358 d->fileName = fileName;
359 d->rootPath = QFileInfo(fileName).absolutePath();
360 QFile file(fileName);
361 if (!file.open(QIODevice::ReadOnly)) {
362 d->errorMsg = QCoreApplication::translate(
"QHelpProject",
363 "The input file %1 could not be opened.").arg(fileName);
367 d->readData(file.readAll());
368 return !d->hasError();
372
373
374
375
376
380 return d->errorString();
385
386
389 return d->namespaceName;
393
394
397 return d->virtualFolder;
401
402
405 return d->customFilterList;
409
410
413 return d->filterSectionList;
417
418
425
426
void readData(const QByteArray &contents)
QList< QHelpDataFilterSection > filterSectionList
QMap< QString, QVariant > metaData
QList< QHelpDataCustomFilter > customFilterList
The QHelpProjectData class stores all information found in a Qt help project file.
QHelpProjectData()
Constructs a Qt help project data structure.
~QHelpProjectData()
Destroys the help project data.
QString virtualFolder() const
QMap< QString, QVariant > metaData() const
QString errorMessage() const
Returns an error message if the reading of the Qt help project file failed.
QString namespaceName() const
QList< QHelpDataFilterSection > filterSections() const
bool readData(const QString &fileName)
Reads the file fileName and stores the help data.
QList< QHelpDataCustomFilter > customFilters() const
Combined button and popup list for selecting options.
static QString msgMissingAttribute(const QString &fileName, qint64 lineNumber, const QString &name)