diff options
Diffstat (limited to 'src/qmlcompiler/qqmljsresourcefilemapper.cpp')
-rw-r--r-- | src/qmlcompiler/qqmljsresourcefilemapper.cpp | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/src/qmlcompiler/qqmljsresourcefilemapper.cpp b/src/qmlcompiler/qqmljsresourcefilemapper.cpp new file mode 100644 index 0000000000..4213902fb3 --- /dev/null +++ b/src/qmlcompiler/qqmljsresourcefilemapper.cpp @@ -0,0 +1,264 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "qqmljsresourcefilemapper_p.h" + +#include <QFileInfo> +#include <QDir> +#include <QXmlStreamReader> + +QT_BEGIN_NAMESPACE + +QQmlJSResourceFileMapper::Filter QQmlJSResourceFileMapper::allQmlJSFilter() { + return Filter { + QString(), + QStringList { QStringLiteral("qml"), QStringLiteral("js"), QStringLiteral("mjs") }, + Directory | Recurse + }; +} + +QQmlJSResourceFileMapper::Filter QQmlJSResourceFileMapper::localFileFilter(const QString &file) +{ + return Filter { + QFileInfo(file).canonicalFilePath(), + QStringList(), + File + }; +} + +QQmlJSResourceFileMapper::Filter QQmlJSResourceFileMapper::resourceFileFilter(const QString &file) +{ + return Filter { + file, + QStringList(), + Resource + }; +} + +QQmlJSResourceFileMapper::Filter QQmlJSResourceFileMapper::resourceQmlDirectoryFilter( + const QString &directory) +{ + return Filter { + directory, + QStringList { QStringLiteral("qml") }, + Directory | Resource + }; +} + +QQmlJSResourceFileMapper::QQmlJSResourceFileMapper(const QStringList &resourceFiles) +{ + for (const QString &fileName: resourceFiles) { + QFile f(fileName); + if (!f.open(QIODevice::ReadOnly)) + continue; + populateFromQrcFile(f); + } +} + +bool QQmlJSResourceFileMapper::isEmpty() const +{ + return qrcPathToFileSystemPath.isEmpty(); +} + +bool QQmlJSResourceFileMapper::isFile(const QString &resourcePath) const +{ + for (const auto &entry : qrcPathToFileSystemPath) { + if (entry.resourcePath == resourcePath) + return true; + } + return false; +} + +static bool hasSuffix(const QString &qrcPath, const QStringList &suffixes) +{ + if (suffixes.isEmpty()) + return true; + const QString suffix = QFileInfo(qrcPath).suffix(); + return suffixes.contains(suffix); +} + +template<typename HandleMatch> +void doFilter(const QList<QQmlJSResourceFileMapper::Entry> &qrcPathToFileSystemPath, + const QQmlJSResourceFileMapper::Filter &filter, + const HandleMatch &handler) +{ + if (filter.flags & QQmlJSResourceFileMapper::Directory) { + const QString terminatedDirectory = filter.path.endsWith(u'/') + ? filter.path : (filter.path + u'/'); + + for (auto it = qrcPathToFileSystemPath.constBegin(), + end = qrcPathToFileSystemPath.constEnd(); it != end; ++it) { + + const QString candidate = (filter.flags & QQmlJSResourceFileMapper::Resource) + ? it->resourcePath + : it->filePath; + + if (!filter.path.isEmpty() && !candidate.startsWith(terminatedDirectory)) + continue; + + if (!hasSuffix(candidate, filter.suffixes)) + continue; + + if ((filter.flags & QQmlJSResourceFileMapper::Recurse) + // Crude. But shall we really allow slashes in QRC file names? + || !candidate.mid(terminatedDirectory.size()).contains(u'/')) { + if (handler(*it)) + return; + } + } + return; + } + + if (!hasSuffix(filter.path, filter.suffixes)) + return; + + for (auto it = qrcPathToFileSystemPath.constBegin(), + end = qrcPathToFileSystemPath.constEnd(); it != end; ++it) { + if (filter.flags & QQmlJSResourceFileMapper::Resource) { + if (it->resourcePath == filter.path && handler(*it)) + return; + } else if (it->filePath == filter.path && handler(*it)) { + return; + } + } +} + +QList<QQmlJSResourceFileMapper::Entry> QQmlJSResourceFileMapper::filter( + const QQmlJSResourceFileMapper::Filter &filter) const +{ + QList<Entry> result; + doFilter(qrcPathToFileSystemPath, filter, [&](const Entry &entry) { + result.append(entry); + return false; + }); + return result; +} + +QStringList QQmlJSResourceFileMapper::filePaths( + const QQmlJSResourceFileMapper::Filter &filter) const +{ + QStringList result; + doFilter(qrcPathToFileSystemPath, filter, [&](const Entry &entry) { + result.append(entry.filePath); + return false; + }); + return result; +} + +QStringList QQmlJSResourceFileMapper::resourcePaths( + const QQmlJSResourceFileMapper::Filter &filter) const +{ + QStringList result; + doFilter(qrcPathToFileSystemPath, filter, [&](const Entry &entry) { + result.append(entry.resourcePath); + return false; + }); + return result; +} + +QQmlJSResourceFileMapper::Entry QQmlJSResourceFileMapper::entry( + const QQmlJSResourceFileMapper::Filter &filter) const +{ + Entry result; + doFilter(qrcPathToFileSystemPath, filter, [&](const Entry &entry) { + result = entry; + return true; + }); + return result; +} + +void QQmlJSResourceFileMapper::populateFromQrcFile(QFile &file) +{ + enum State { + InitialState, + InRCC, + InResource, + InFile + }; + State state = InitialState; + + QDir qrcDir = QFileInfo(file).absoluteDir(); + + QString prefix; + QString currentFileName; + QXmlStreamAttributes currentFileAttributes; + + QXmlStreamReader reader(&file); + while (!reader.atEnd()) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement: + if (reader.name() == QStringLiteral("RCC")) { + if (state != InitialState) + return; + state = InRCC; + continue; + } else if (reader.name() == QStringLiteral("qresource")) { + if (state != InRCC) + return; + state = InResource; + QXmlStreamAttributes attributes = reader.attributes(); + if (attributes.hasAttribute(QStringLiteral("prefix"))) + prefix = attributes.value(QStringLiteral("prefix")).toString(); + if (!prefix.startsWith(QLatin1Char('/'))) + prefix.prepend(QLatin1Char('/')); + if (!prefix.endsWith(QLatin1Char('/'))) + prefix.append(QLatin1Char('/')); + continue; + } else if (reader.name() == QStringLiteral("file")) { + if (state != InResource) + return; + state = InFile; + currentFileAttributes = reader.attributes(); + continue; + } + return; + + case QXmlStreamReader::EndElement: + if (reader.name() == QStringLiteral("file")) { + if (state != InFile) + return; + state = InResource; + continue; + } else if (reader.name() == QStringLiteral("qresource")) { + if (state != InResource) + return; + state = InRCC; + continue; + } else if (reader.name() == QStringLiteral("RCC")) { + if (state != InRCC) + return; + state = InitialState; + continue; + } + return; + + case QXmlStreamReader::Characters: { + if (reader.isWhitespace()) + break; + if (state != InFile) + return; + currentFileName = reader.text().toString(); + if (currentFileName.isEmpty()) + continue; + + const QString fsPath = QDir::cleanPath(qrcDir.absoluteFilePath(currentFileName)); + + if (currentFileAttributes.hasAttribute(QStringLiteral("alias"))) + currentFileName = currentFileAttributes.value(QStringLiteral("alias")).toString(); + + currentFileName = QDir::cleanPath(currentFileName); + while (currentFileName.startsWith(QLatin1String("../"))) + currentFileName.remove(0, 3); + + const QString qrcPath = prefix + currentFileName; + if (QFile::exists(fsPath)) + qrcPathToFileSystemPath.append({qrcPath, fsPath}); + continue; + } + + default: break; + } + } +} + +QT_END_NAMESPACE |