Move examples manifest parser in separate function

and file. To make it auto-testable.

Change-Id: I19d263bf080a0089eb9a4ec0f379c52446771c0a
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Eike Ziller
2023-02-21 14:11:40 +01:00
parent 00fd4c8fea
commit 7e75097447
7 changed files with 330 additions and 238 deletions

View File

@@ -3,6 +3,7 @@
#include "exampleslistmodel.h"
#include "examplesparser.h"
#include "qtsupporttr.h"
#include <QBuffer>
@@ -288,38 +289,11 @@ ExamplesViewController::ExamplesViewController(ExampleSetModel *exampleSetModel,
updateExamples();
}
static QString fixStringForTags(const QString &string)
{
QString returnString = string;
returnString.remove(QLatin1String("<i>"));
returnString.remove(QLatin1String("</i>"));
returnString.remove(QLatin1String("<tt>"));
returnString.remove(QLatin1String("</tt>"));
return returnString;
}
static QStringList trimStringList(const QStringList &stringlist)
{
return Utils::transform(stringlist, [](const QString &str) { return str.trimmed(); });
}
static QString relativeOrInstallPath(const QString &path, const QString &manifestPath,
const QString &installPath)
{
const QChar slash = QLatin1Char('/');
const QString relativeResolvedPath = manifestPath + slash + path;
const QString installResolvedPath = installPath + slash + path;
if (QFile::exists(relativeResolvedPath))
return relativeResolvedPath;
if (QFile::exists(installResolvedPath))
return installResolvedPath;
// doesn't exist, just return relative
return relativeResolvedPath;
}
static bool isValidExampleOrDemo(ExampleItem *item)
{
QTC_ASSERT(item, return false);
if (item->type == Tutorial)
return true;
static QString invalidPrefix = QLatin1String("qthelp:////"); /* means that the qthelp url
doesn't have any namespace */
QString reason;
@@ -345,158 +319,6 @@ static bool isValidExampleOrDemo(ExampleItem *item)
return ok || debugExamples();
}
static QList<ExampleItem *> parseExamples(QXmlStreamReader *reader,
const QString &projectsOffset,
const QString &examplesInstallPath)
{
QList<ExampleItem *> result;
std::unique_ptr<ExampleItem> item;
const QChar slash = QLatin1Char('/');
while (!reader->atEnd()) {
switch (reader->readNext()) {
case QXmlStreamReader::StartElement:
if (reader->name() == QLatin1String("example")) {
item = std::make_unique<ExampleItem>();
item->type = Example;
QXmlStreamAttributes attributes = reader->attributes();
item->name = attributes.value(QLatin1String("name")).toString();
item->projectPath = attributes.value(QLatin1String("projectPath")).toString();
item->hasSourceCode = !item->projectPath.isEmpty();
item->projectPath = relativeOrInstallPath(item->projectPath, projectsOffset, examplesInstallPath);
item->imageUrl = attributes.value(QLatin1String("imageUrl")).toString();
QPixmapCache::remove(item->imageUrl);
item->docUrl = attributes.value(QLatin1String("docUrl")).toString();
item->isHighlighted = attributes.value(QLatin1String("isHighlighted")).toString() == QLatin1String("true");
} else if (reader->name() == QLatin1String("fileToOpen")) {
const QString mainFileAttribute = reader->attributes().value(
QLatin1String("mainFile")).toString();
const QString filePath = relativeOrInstallPath(
reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement),
projectsOffset, examplesInstallPath);
item->filesToOpen.append(filePath);
if (mainFileAttribute.compare(QLatin1String("true"), Qt::CaseInsensitive) == 0)
item->mainFile = filePath;
} else if (reader->name() == QLatin1String("description")) {
item->description = fixStringForTags(reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement));
} else if (reader->name() == QLatin1String("dependency")) {
item->dependencies.append(projectsOffset + slash + reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement));
} else if (reader->name() == QLatin1String("tags")) {
item->tags = trimStringList(reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).split(QLatin1Char(','), Qt::SkipEmptyParts));
} else if (reader->name() == QLatin1String("platforms")) {
item->platforms = trimStringList(reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).split(QLatin1Char(','), Qt::SkipEmptyParts));
}
break;
case QXmlStreamReader::EndElement:
if (reader->name() == QLatin1String("example")) {
if (isValidExampleOrDemo(item.get()))
result.push_back(item.release());
} else if (reader->name() == QLatin1String("examples")) {
return result;
}
break;
default: // nothing
break;
}
}
return result;
}
static QList<ExampleItem *> parseDemos(QXmlStreamReader *reader,
const QString &projectsOffset,
const QString &demosInstallPath)
{
QList<ExampleItem *> result;
std::unique_ptr<ExampleItem> item;
const QChar slash = QLatin1Char('/');
while (!reader->atEnd()) {
switch (reader->readNext()) {
case QXmlStreamReader::StartElement:
if (reader->name() == QLatin1String("demo")) {
item = std::make_unique<ExampleItem>();
item->type = Demo;
QXmlStreamAttributes attributes = reader->attributes();
item->name = attributes.value(QLatin1String("name")).toString();
item->projectPath = attributes.value(QLatin1String("projectPath")).toString();
item->hasSourceCode = !item->projectPath.isEmpty();
item->projectPath = relativeOrInstallPath(item->projectPath, projectsOffset, demosInstallPath);
item->imageUrl = attributes.value(QLatin1String("imageUrl")).toString();
QPixmapCache::remove(item->imageUrl);
item->docUrl = attributes.value(QLatin1String("docUrl")).toString();
item->isHighlighted = attributes.value(QLatin1String("isHighlighted")).toString() == QLatin1String("true");
} else if (reader->name() == QLatin1String("fileToOpen")) {
item->filesToOpen.append(relativeOrInstallPath(reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement),
projectsOffset, demosInstallPath));
} else if (reader->name() == QLatin1String("description")) {
item->description = fixStringForTags(reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement));
} else if (reader->name() == QLatin1String("dependency")) {
item->dependencies.append(projectsOffset + slash + reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement));
} else if (reader->name() == QLatin1String("tags")) {
item->tags = reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).split(QLatin1Char(','));
}
break;
case QXmlStreamReader::EndElement:
if (reader->name() == QLatin1String("demo")) {
if (isValidExampleOrDemo(item.get()))
result.push_back(item.release());
} else if (reader->name() == QLatin1String("demos")) {
return result;
}
break;
default: // nothing
break;
}
}
return result;
}
static QList<ExampleItem *> parseTutorials(QXmlStreamReader *reader, const QString &projectsOffset)
{
QList<ExampleItem *> result;
std::unique_ptr<ExampleItem> item = std::make_unique<ExampleItem>();
const QChar slash = QLatin1Char('/');
while (!reader->atEnd()) {
switch (reader->readNext()) {
case QXmlStreamReader::StartElement:
if (reader->name() == QLatin1String("tutorial")) {
item = std::make_unique<ExampleItem>();
item->type = Tutorial;
QXmlStreamAttributes attributes = reader->attributes();
item->name = attributes.value(QLatin1String("name")).toString();
item->projectPath = attributes.value(QLatin1String("projectPath")).toString();
item->hasSourceCode = !item->projectPath.isEmpty();
item->projectPath.prepend(slash);
item->projectPath.prepend(projectsOffset);
item->imageUrl = Utils::StyleHelper::dpiSpecificImageFile(
attributes.value(QLatin1String("imageUrl")).toString());
QPixmapCache::remove(item->imageUrl);
item->docUrl = attributes.value(QLatin1String("docUrl")).toString();
item->isVideo = attributes.value(QLatin1String("isVideo")).toString() == QLatin1String("true");
item->videoUrl = attributes.value(QLatin1String("videoUrl")).toString();
item->videoLength = attributes.value(QLatin1String("videoLength")).toString();
} else if (reader->name() == QLatin1String("fileToOpen")) {
item->filesToOpen.append(projectsOffset + slash + reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement));
} else if (reader->name() == QLatin1String("description")) {
item->description = fixStringForTags(reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement));
} else if (reader->name() == QLatin1String("dependency")) {
item->dependencies.append(projectsOffset + slash + reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement));
} else if (reader->name() == QLatin1String("tags")) {
item->tags = reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).split(QLatin1Char(','));
}
break;
case QXmlStreamReader::EndElement:
if (reader->name() == QLatin1String("tutorial"))
result.push_back(item.release());
else if (reader->name() == QLatin1String("tutorials"))
return result;
break;
default: // nothing
break;
}
}
return result;
}
void ExamplesViewController::updateExamples()
{
QString examplesInstallPath;
@@ -509,41 +331,23 @@ void ExamplesViewController::updateExamples()
QList<ExampleItem *> items;
for (const QString &exampleSource : sources) {
QFile exampleFile(exampleSource);
if (!exampleFile.open(QIODevice::ReadOnly)) {
if (debugExamples())
qWarning() << "ERROR: Could not open file" << exampleSource;
if (debugExamples()) {
qWarning() << QString::fromLatin1("Reading file \"%1\"...")
.arg(QFileInfo(exampleSource).absoluteFilePath());
}
const expected_str<QList<ExampleItem *>> result
= parseExamples(exampleSource, examplesInstallPath, demosInstallPath, m_isExamples);
if (!result) {
if (debugExamples()) {
qWarning() << "ERROR: Could not read examples from" << exampleSource << ":"
<< result.error();
}
continue;
}
QFileInfo fi(exampleSource);
QString offsetPath = fi.path();
QDir examplesDir(offsetPath);
QDir demosDir(offsetPath);
if (debugExamples())
qWarning() << QString::fromLatin1("Reading file \"%1\"...").arg(fi.absoluteFilePath());
QXmlStreamReader reader(&exampleFile);
while (!reader.atEnd())
switch (reader.readNext()) {
case QXmlStreamReader::StartElement:
if (m_isExamples && reader.name() == QLatin1String("examples"))
items += parseExamples(&reader, examplesDir.path(), examplesInstallPath);
else if (m_isExamples && reader.name() == QLatin1String("demos"))
items += parseDemos(&reader, demosDir.path(), demosInstallPath);
else if (!m_isExamples && reader.name() == QLatin1String("tutorials"))
items += parseTutorials(&reader, examplesDir.path());
break;
default: // nothing
break;
}
if (reader.hasError() && debugExamples()) {
qWarning().noquote().nospace() << "ERROR: Could not parse file as XML document ("
<< exampleSource << "):" << reader.lineNumber() << ':' << reader.columnNumber()
<< ": " << reader.errorString();
}
items += filtered(*result, isValidExampleOrDemo);
}
if (m_isExamples) {
if (m_exampleSetModel->selectedQtSupports(Android::Constants::ANDROID_DEVICE_TYPE)) {
items = Utils::filtered(items, [](ExampleItem *item) {