From c713a28c433298e2af8457d603c0b8753c60fd59 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Thu, 29 Aug 2024 13:57:20 +0200 Subject: [PATCH] Update mime database from Qt qtbase/4e9944e6c8a456353d243ab268cb0f01ff006faa QMimeDatabase: collect glob patterns from all locations The original patch needs some adaptions for Qt Creator's custom setPreferredSuffix and setGlobPatternsForMimeType Change-Id: I9a7a2cbe79bd732a63f3a88fc81ff4d0b18c75c8 Reviewed-by: Jarek Kobus --- src/libs/utils/mimetypes2/mimedatabase.cpp | 107 +++++++----- src/libs/utils/mimetypes2/mimedatabase_p.h | 11 +- src/libs/utils/mimetypes2/mimeprovider.cpp | 172 +++++++++++-------- src/libs/utils/mimetypes2/mimeprovider_p.h | 58 ++++--- src/libs/utils/mimetypes2/mimetype.cpp | 77 ++------- src/libs/utils/mimetypes2/mimetype.h | 1 + src/libs/utils/mimetypes2/mimetype_p.h | 40 +---- src/libs/utils/mimetypes2/mimetypeparser.cpp | 20 ++- src/libs/utils/mimetypes2/mimetypeparser_p.h | 21 ++- 9 files changed, 265 insertions(+), 242 deletions(-) diff --git a/src/libs/utils/mimetypes2/mimedatabase.cpp b/src/libs/utils/mimetypes2/mimedatabase.cpp index b7de9f207f2..b7a8459af89 100644 --- a/src/libs/utils/mimetypes2/mimedatabase.cpp +++ b/src/libs/utils/mimetypes2/mimedatabase.cpp @@ -77,10 +77,12 @@ bool MimeDatabasePrivate::shouldCheck() return m_forceLoad; } +#if 0 static QStringList locateMimeDirectories() { return QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("mime"), QStandardPaths::LocateDirectory); } +#endif #if defined(Q_OS_UNIX) && !defined(Q_OS_NACL) && !defined(Q_OS_INTEGRITY) # define QT_USE_MMAP @@ -246,9 +248,8 @@ MimeType MimeDatabasePrivate::mimeTypeForName(const QString &nameOrAlias) { const QString mimeName = resolveAlias(nameOrAlias); for (const auto &provider : providers()) { - MimeType mime = provider->mimeTypeForName(mimeName); - if (mime.isValid()) - return mime; + if (provider->knowsMimeType(mimeName)) + return MimeType(MimeTypePrivate(mimeName)); } return {}; } @@ -273,54 +274,70 @@ MimeGlobMatchResult MimeDatabasePrivate::findByFileName(const QString &fileName) return result; } -void MimeDatabasePrivate::loadMimeTypePrivate(MimeTypePrivate &mimePrivate) +MimeTypePrivate::LocaleHash MimeDatabasePrivate::localeComments(const QString &name) { QMutexLocker locker(&mutex); - if (mimePrivate.name.isEmpty()) - return; // invalid mimetype - if (!mimePrivate.loaded) { // XML provider sets loaded=true, binary provider does this on demand - Q_ASSERT(mimePrivate.fromCache); - bool found = false; - for (const auto &provider : providers()) { - if (provider->loadMimeTypePrivate(mimePrivate)) { - found = true; - break; - } - } - if (!found) { - const QString file = mimePrivate.name + ".xml"_L1; - qWarning() << "No file found for" << file << ", even though update-mime-info said it would exist.\n" - "Either it was just removed, or the directory doesn't have executable permission..." - << locateMimeDirectories(); - } - mimePrivate.loaded = true; + for (const auto &provider : providers()) { + auto comments = provider->localeComments(name); + if (!comments.isEmpty()) + return comments; // maybe we want to merge in comments from more global providers, in + // case of more translations? } + return {}; } -void MimeDatabasePrivate::loadGenericIcon(MimeTypePrivate &mimePrivate) +QStringList MimeDatabasePrivate::globPatterns(const QString &name) { QMutexLocker locker(&mutex); - if (mimePrivate.fromCache) { - mimePrivate.genericIconName.clear(); - for (const auto &provider : providers()) { - provider->loadGenericIcon(mimePrivate); - if (!mimePrivate.genericIconName.isEmpty()) - break; - } + QStringList patterns; + const auto &providerList = providers(); + // reverse iteration because we start from most global, add up, clear if delete-all, and add up + // again. + for (auto rit = providerList.rbegin(); rit != providerList.rend(); ++rit) { + auto *provider = rit->get(); + if (provider->hasGlobDeleteAll(name)) + patterns.clear(); + // handle MIME type overriding from Qt Creator + if (!provider->m_overriddenMimeTypes.contains(name)) + patterns += provider->globPatterns(name); } + + // added for Qt Creator, overriding preferred suffix (first pattern in the list) + const QString preferredSuffix = m_preferredSuffix.value(name); + if (!preferredSuffix.isEmpty()) { + auto it = std::find_if(patterns.begin(), + patterns.end(), + [preferredSuffix](const QString &pattern) { + return MimeType::suffixFromPattern(pattern) == preferredSuffix; + }); + if (it != patterns.end()) + patterns.erase(it); + patterns.prepend(QLatin1String("*.") + preferredSuffix); + } + + return patterns; } -void MimeDatabasePrivate::loadIcon(MimeTypePrivate &mimePrivate) +QString MimeDatabasePrivate::genericIcon(const QString &name) { QMutexLocker locker(&mutex); - if (mimePrivate.fromCache) { - mimePrivate.iconName.clear(); - for (const auto &provider : providers()) { - provider->loadIcon(mimePrivate); - if (!mimePrivate.iconName.isEmpty()) - break; - } + for (const auto &provider : providers()) { + QString genericIconName = provider->genericIcon(name); + if (!genericIconName.isEmpty()) + return genericIconName; } + return {}; +} + +QString MimeDatabasePrivate::icon(const QString &name) +{ + QMutexLocker locker(&mutex); + for (const auto &provider : providers()) { + QString iconName = provider->icon(name); + if (!iconName.isEmpty()) + return iconName; + } + return {}; } QString MimeDatabasePrivate::fallbackParent(const QString &mimeTypeName) const @@ -411,12 +428,12 @@ MimeType MimeDatabasePrivate::findByData(const QByteArray &data, int *accuracyPt #endif *accuracyPtr = 0; - MimeType candidate; + QString candidate; for (const auto &provider : providers()) - provider->findByMagic(data, accuracyPtr, candidate); + provider->findByMagic(data, accuracyPtr, &candidate); - if (candidate.isValid()) - return candidate; + if (!candidate.isEmpty()) + return MimeType(MimeTypePrivate(candidate)); if (isTextFile(data)) { *accuracyPtr = 5; @@ -937,6 +954,12 @@ void MimeDatabasePrivate::setGlobPatternsForMimeType(const MimeType &mimeType, } } +void MimeDatabasePrivate::setPreferredSuffix(const QString &mimeName, const QString &suffix) +{ + QMutexLocker locker(&mutex); + m_preferredSuffix.insert(mimeName, suffix); +} + void MimeDatabasePrivate::checkInitPhase(const QString &info) { QReadLocker locker(&m_initMutex); diff --git a/src/libs/utils/mimetypes2/mimedatabase_p.h b/src/libs/utils/mimetypes2/mimedatabase_p.h index c608b8d7efb..5a6c2d35b8e 100644 --- a/src/libs/utils/mimetypes2/mimedatabase_p.h +++ b/src/libs/utils/mimetypes2/mimedatabase_p.h @@ -70,9 +70,10 @@ public: MimeGlobMatchResult findByFileName(const QString &fileName); // API for MimeType. Takes care of locking the mutex. - void loadMimeTypePrivate(MimeTypePrivate &mimePrivate); - void loadGenericIcon(MimeTypePrivate &mimePrivate); - void loadIcon(MimeTypePrivate &mimePrivate); + MimeTypePrivate::LocaleHash localeComments(const QString &name); + QStringList globPatterns(const QString &name); + QString genericIcon(const QString &name); + QString icon(const QString &name); QStringList mimeParents(const QString &mimeName); QStringList listAliases(const QString &mimeName); bool mimeInherits(const QString &mime, const QString &parent); @@ -83,6 +84,7 @@ public: void setMagicRulesForMimeType(const MimeType &mimeType, const QMap> &rules); void setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns); + void setPreferredSuffix(const QString &mimeName, const QString &suffix); void checkInitPhase(const QString &info); void addInitializer(const std::function &init); @@ -94,7 +96,7 @@ private: QString fallbackParent(const QString &mimeTypeName) const; const QString m_defaultMimeType; - mutable Providers m_providers; + mutable Providers m_providers; // most local first, most global last QElapsedTimer m_lastCheck; // added for Qt Creator @@ -109,6 +111,7 @@ public: QReadWriteLock m_initMutex; std::atomic_bool m_initialized = false; int m_startupPhase = 0; + QHash m_preferredSuffix; // MIME name -> suffix }; } // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimeprovider.cpp b/src/libs/utils/mimetypes2/mimeprovider.cpp index 29aee49e87d..effbc2c5042 100644 --- a/src/libs/utils/mimetypes2/mimeprovider.cpp +++ b/src/libs/utils/mimetypes2/mimeprovider.cpp @@ -186,25 +186,11 @@ void MimeBinaryProvider::ensureLoaded() m_cacheFile.reset(); } -static MimeType mimeTypeForNameUnchecked(const QString &name) -{ - MimeTypePrivate data; - data.name = name; - data.fromCache = true; - // The rest is retrieved on demand. - // comment and globPatterns: in loadMimeTypePrivate - // iconName: in loadIcon - // genericIconName: in loadGenericIcon - return MimeType(data); -} - -MimeType MimeBinaryProvider::mimeTypeForName(const QString &name) +bool MimeBinaryProvider::knowsMimeType(const QString &name) { if (!m_mimetypeListLoaded) loadMimeTypeList(); - if (!m_mimetypeNames.contains(name)) - return MimeType(); // unknown mimetype - return mimeTypeForNameUnchecked(name); + return m_mimetypeNames.contains(name); } void MimeBinaryProvider::addFileNameMatches(const QString &fileName, MimeGlobMatchResult &result) @@ -368,7 +354,7 @@ bool MimeBinaryProvider::matchMagicRule(MimeBinaryProvider::CacheFile *cacheFile return false; } -void MimeBinaryProvider::findByMagic(const QByteArray &data, int *accuracyPtr, MimeType &candidate) +void MimeBinaryProvider::findByMagic(const QByteArray &data, int *accuracyPtr, QString *candidate) { const int magicListOffset = m_cacheFile->getUint32(PosMagicListOffset); const int numMatches = m_cacheFile->getUint32(magicListOffset); @@ -387,7 +373,7 @@ void MimeBinaryProvider::findByMagic(const QByteArray &data, int *accuracyPtr, M *accuracyPtr = m_cacheFile->getUint32(off); // Return the first match. We have no rules for conflicting magic data... // (mime.cache itself is sorted, but what about local overrides with a lower prio?) - candidate = mimeTypeForNameUnchecked(QLatin1StringView(mimeType)); + *candidate = QString::fromLatin1(mimeType); return; } } @@ -496,35 +482,63 @@ void MimeBinaryProvider::addAllMimeTypes(QList &result) if (result.isEmpty()) { result.reserve(m_mimetypeNames.size()); for (const QString &name : std::as_const(m_mimetypeNames)) - result.append(mimeTypeForNameUnchecked(name)); + result.append(MimeType(MimeTypePrivate(name))); } else { for (const QString &name : std::as_const(m_mimetypeNames)) if (std::find_if(result.constBegin(), result.constEnd(), [name](const MimeType &mime) -> bool { return mime.name() == name; }) == result.constEnd()) - result.append(mimeTypeForNameUnchecked(name)); + result.append(MimeType(MimeTypePrivate(name))); } } -bool MimeBinaryProvider::loadMimeTypePrivate(MimeTypePrivate &data) +MimeTypePrivate::LocaleHash MimeBinaryProvider::localeComments(const QString &name) +{ + MimeTypeExtraMap::const_iterator it = loadMimeTypeExtra(name); + if (it != m_mimetypeExtra.constEnd()) { + const MimeTypeExtra &e = it.value(); + return e.localeComments; + } + return {}; +} + +bool MimeBinaryProvider::hasGlobDeleteAll(const QString &name) +{ + MimeTypeExtraMap::const_iterator it = loadMimeTypeExtra(name); + if (it != m_mimetypeExtra.constEnd()) { + const MimeTypeExtra &e = it.value(); + return e.hasGlobDeleteAll; + } + return {}; +} + +QStringList MimeBinaryProvider::globPatterns(const QString &name) +{ + MimeTypeExtraMap::const_iterator it = loadMimeTypeExtra(name); + if (it != m_mimetypeExtra.constEnd()) { + const MimeTypeExtra &e = it.value(); + return e.globPatterns; + } + return {}; +} + +MimeBinaryProvider::MimeTypeExtraMap::const_iterator +MimeBinaryProvider::loadMimeTypeExtra(const QString &mimeName) { #if QT_CONFIG(xmlstreamreader) - if (data.loaded) - return true; - - auto it = m_mimetypeExtra.constFind(data.name); + auto it = m_mimetypeExtra.constFind(mimeName); if (it == m_mimetypeExtra.constEnd()) { // load comment and globPatterns // shared-mime-info since 1.3 lowercases the xml files - QString mimeFile = m_directory + u'/' + data.name.toLower() + ".xml"_L1; + QString mimeFile = m_directory + u'/' + mimeName.toLower() + ".xml"_L1; if (!QFileInfo::exists(mimeFile)) - mimeFile = m_directory + u'/' + data.name + ".xml"_L1; // pre-1.3 + mimeFile = m_directory + u'/' + mimeName + ".xml"_L1; // pre-1.3 QFile qfile(mimeFile); if (!qfile.open(QFile::ReadOnly)) - return false; + return m_mimetypeExtra.constEnd(); - auto insertIt = m_mimetypeExtra.insert(data.name, MimeTypeExtra{}); + auto insertIt = m_mimetypeExtra.insert(mimeName, MimeTypeExtra{}); it = insertIt; MimeTypeExtra &extra = insertIt.value(); QString mainPattern; @@ -532,13 +546,13 @@ bool MimeBinaryProvider::loadMimeTypePrivate(MimeTypePrivate &data) QXmlStreamReader xml(&qfile); if (xml.readNextStartElement()) { if (xml.name() != "mime-type"_L1) { - return false; + return m_mimetypeExtra.constEnd(); } const auto name = xml.attributes().value("type"_L1); if (name.isEmpty()) - return false; - if (name.compare(data.name, Qt::CaseInsensitive)) - qWarning() << "Got name" << name << "in file" << mimeFile << "expected" << data.name; + return m_mimetypeExtra.constEnd(); + if (name.compare(mimeName, Qt::CaseInsensitive)) + qWarning() << "Got name" << name << "in file" << mimeFile << "expected" << mimeName; while (xml.readNextStartElement()) { const auto tag = xml.name(); @@ -551,8 +565,7 @@ bool MimeBinaryProvider::loadMimeTypePrivate(MimeTypePrivate &data) extra.localeComments.insert(lang, text); continue; // we called readElementText, so we're at the EndElement already. } else if (tag == "glob-deleteall"_L1) { // as written out by shared-mime-info >= 0.70 - extra.globPatterns.clear(); - mainPattern.clear(); + extra.hasGlobDeleteAll = true; } else if (tag == "glob"_L1) { // as written out by shared-mime-info >= 0.70 const QString pattern = xml.attributes().value("pattern"_L1).toString(); if (mainPattern.isEmpty() && pattern.startsWith(u'*')) { @@ -574,14 +587,11 @@ bool MimeBinaryProvider::loadMimeTypePrivate(MimeTypePrivate &data) extra.globPatterns.prepend(mainPattern); } } - const MimeTypeExtra &e = it.value(); - data.localeComments = e.localeComments; - data.globPatterns = e.globPatterns; - return true; + return it; #else - Q_UNUSED(data); + Q_UNUSED(mimeName); qWarning("Cannot load mime type since QXmlStreamReader is not available."); - return false; + return m_mimetypeExtra.constEnd(); #endif // feature xmlstreamreader } @@ -611,22 +621,16 @@ QLatin1StringView MimeBinaryProvider::iconForMime(CacheFile *cacheFile, int posL return QLatin1StringView(); } -void MimeBinaryProvider::loadIcon(MimeTypePrivate &data) +QString MimeBinaryProvider::icon(const QString &name) { - const QByteArray inputMime = data.name.toLatin1(); - const QLatin1StringView icon = iconForMime(m_cacheFile.get(), PosIconsListOffset, inputMime); - if (!icon.isEmpty()) { - data.iconName = icon; - } + const QByteArray inputMime = name.toLatin1(); + return iconForMime(m_cacheFile.get(), PosIconsListOffset, inputMime); } -void MimeBinaryProvider::loadGenericIcon(MimeTypePrivate &data) +QString MimeBinaryProvider::genericIcon(const QString &name) { - const QByteArray inputMime = data.name.toLatin1(); - const QLatin1StringView icon = iconForMime(m_cacheFile.get(), PosGenericIconsListOffset, inputMime); - if (!icon.isEmpty()) { - data.genericIconName = icon; - } + const QByteArray inputMime = name.toLatin1(); + return iconForMime(m_cacheFile.get(), PosGenericIconsListOffset, inputMime); } //// @@ -722,9 +726,9 @@ bool MimeXMLProvider::isInternalDatabase() const #endif } -MimeType MimeXMLProvider::mimeTypeForName(const QString &name) +bool MimeXMLProvider::knowsMimeType(const QString &name) { - return m_nameMimeTypeMap.value(name); + return m_nameMimeTypeMap.contains(name); } void MimeXMLProvider::addFileNameMatches(const QString &fileName, MimeGlobMatchResult &result) @@ -732,10 +736,8 @@ void MimeXMLProvider::addFileNameMatches(const QString &fileName, MimeGlobMatchR m_mimeTypeGlobs.matchingGlobs(fileName, result, m_overriddenMimeTypes); } -void MimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr, MimeType &candidate) +void MimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr, QString *candidate) { - QString candidateName; - bool foundOne = false; for (const MimeMagicRuleMatcher &matcher : std::as_const(m_magicMatchers)) { if (m_overriddenMimeTypes.contains(matcher.mimetype())) continue; @@ -743,13 +745,10 @@ void MimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr, Mime const int priority = matcher.priority(); if (priority > *accuracyPtr) { *accuracyPtr = priority; - candidateName = matcher.mimetype(); - foundOne = true; + *candidate = matcher.mimetype(); } } } - if (foundOne) - candidate = mimeTypeForName(candidateName); } void MimeXMLProvider::ensureLoaded() @@ -779,6 +778,31 @@ void MimeXMLProvider::ensureLoaded() load(file); } +MimeTypePrivate::LocaleHash MimeXMLProvider::localeComments(const QString &name) +{ + return m_nameMimeTypeMap.value(name).localeComments; +} + +bool MimeXMLProvider::hasGlobDeleteAll(const QString &name) +{ + return m_nameMimeTypeMap.value(name).hasGlobDeleteAll; +} + +QStringList MimeXMLProvider::globPatterns(const QString &name) +{ + return m_nameMimeTypeMap.value(name).globPatterns; +} + +QString MimeXMLProvider::icon(const QString &name) +{ + return m_nameMimeTypeMap.value(name).iconName; +} + +QString MimeXMLProvider::genericIcon(const QString &name) +{ + return m_nameMimeTypeMap.value(name).genericIconName; +} + void MimeXMLProvider::load(const QString &fileName) { QString errorMessage; @@ -820,14 +844,11 @@ void MimeXMLProvider::addGlobPattern(const MimeGlobPattern &glob) m_mimeTypeGlobs.addGlob(glob); } -void MimeXMLProvider::addMimeType(const MimeType &mt) +void MimeXMLProvider::addMimeType(const MimeTypeXMLData &mt) { - Q_ASSERT(!mt.d.data()->fromCache); - - QString name = mt.name(); - if (mt.d->hasGlobDeleteAll) - appendIfNew(m_mimeTypesWithDeletedGlobs, name); - m_nameMimeTypeMap.insert(mt.name(), mt); + if (mt.hasGlobDeleteAll) + appendIfNew(m_mimeTypesWithDeletedGlobs, mt.name); + m_nameMimeTypeMap.insert(mt.name, mt); } /* @@ -843,7 +864,7 @@ void MimeXMLProvider::excludeMimeTypeGlobs(const QStringList &toExclude) for (const auto &mt : toExclude) { auto it = m_nameMimeTypeMap.find(mt); if (it != m_nameMimeTypeMap.end()) - it->d->globPatterns.clear(); + it->globPatterns.clear(); m_mimeTypeGlobs.removeMimeType(mt); } } @@ -883,13 +904,16 @@ void MimeXMLProvider::addAlias(const QString &alias, const QString &name) void MimeXMLProvider::addAllMimeTypes(QList &result) { if (result.isEmpty()) { // fast path - result = m_nameMimeTypeMap.values(); + for (auto it = m_nameMimeTypeMap.constBegin(), end = m_nameMimeTypeMap.constEnd(); + it != end; ++it) { + result.append(MimeType(MimeTypePrivate(it.value().name))); + } } else { for (auto it = m_nameMimeTypeMap.constBegin(), end = m_nameMimeTypeMap.constEnd() ; it != end ; ++it) { const QString newMime = it.key(); if (std::find_if(result.constBegin(), result.constEnd(), [newMime](const MimeType &mime) -> bool { return mime.name() == newMime; }) == result.constEnd()) - result.append(it.value()); + result.append(MimeType(MimeTypePrivate(it.value().name))); } } } @@ -1003,9 +1027,7 @@ void MimeXMLProvider::setGlobPatternsForMimeType(const MimeType &mimeType, // add new patterns as case-insensitive default-weight patterns for (const QString &pattern : patterns) addGlobPattern(MimeGlobPattern(pattern, mimeType.name())); - // the following is safe, because for XML provider mimetype private is always "loaded" - // (see comment in MimeDatabasePrivate::loadMimeTypePrivate) - mimeType.d->globPatterns = patterns; + m_nameMimeTypeMap[mimeType.name()].globPatterns = patterns; } } // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimeprovider_p.h b/src/libs/utils/mimetypes2/mimeprovider_p.h index 2ae3b0c966e..33ffd67725c 100644 --- a/src/libs/utils/mimetypes2/mimeprovider_p.h +++ b/src/libs/utils/mimetypes2/mimeprovider_p.h @@ -25,6 +25,7 @@ namespace Utils { class MimeMagicRuleMatcher; +class MimeTypeXMLData; class MimeProviderBase { @@ -36,18 +37,20 @@ public: virtual bool isValid() = 0; virtual bool isInternalDatabase() const = 0; - virtual MimeType mimeTypeForName(const QString &name) = 0; + virtual bool knowsMimeType(const QString &name) = 0; virtual void addFileNameMatches(const QString &fileName, MimeGlobMatchResult &result) = 0; virtual void addParents(const QString &mime, QStringList &result) = 0; virtual QString resolveAlias(const QString &name) = 0; virtual void addAliases(const QString &name, QStringList &result) = 0; - virtual void findByMagic(const QByteArray &data, int *accuracyPtr, MimeType &candidate) = 0; + virtual void findByMagic(const QByteArray &data, int *accuracyPtr, QString *candidate) = 0; virtual void addAllMimeTypes(QList &result) = 0; - virtual bool loadMimeTypePrivate(MimeTypePrivate &) { return false; } - virtual void loadIcon(MimeTypePrivate &) {} - virtual void loadGenericIcon(MimeTypePrivate &) {} - virtual void ensureLoaded() {} - virtual void excludeMimeTypeGlobs(const QStringList &) {} + virtual MimeTypePrivate::LocaleHash localeComments(const QString &name) = 0; + virtual bool hasGlobDeleteAll(const QString &name) = 0; + virtual QStringList globPatterns(const QString &name) = 0; + virtual QString icon(const QString &name) = 0; + virtual QString genericIcon(const QString &name) = 0; + virtual void ensureLoaded() { } + virtual void excludeMimeTypeGlobs(const QStringList &) { } QString directory() const { return m_directory; } @@ -66,9 +69,9 @@ public: /* MimeTypes with "glob-deleteall" tags are handled differently by each provider sub-class: - - QMimeBinaryProvider parses glob-deleteall tags lazily, i.e. only when loadMimeTypePrivate() + - QMimeBinaryProvider parses glob-deleteall tags lazily, i.e. only when hasGlobDeleteAll() is called, and clears the glob patterns associated with mimetypes that have this tag - - QMimeXMLProvider parses glob-deleteall from the the start, i.e. when a XML file is + - QMimeXMLProvider parses glob-deleteall from the start, i.e. when a XML file is parsed with QMimeTypeParser The two lists below are used to let both provider types (XML and Binary) communicate @@ -104,16 +107,18 @@ public: bool isValid() override; bool isInternalDatabase() const override; - MimeType mimeTypeForName(const QString &name) override; + bool knowsMimeType(const QString &name) override; void addFileNameMatches(const QString &fileName, MimeGlobMatchResult &result) override; void addParents(const QString &mime, QStringList &result) override; QString resolveAlias(const QString &name) override; void addAliases(const QString &name, QStringList &result) override; - void findByMagic(const QByteArray &data, int *accuracyPtr, MimeType &candidate) override; + void findByMagic(const QByteArray &data, int *accuracyPtr, QString *candidate) override; void addAllMimeTypes(QList &result) override; - bool loadMimeTypePrivate(MimeTypePrivate &) override; - void loadIcon(MimeTypePrivate &) override; - void loadGenericIcon(MimeTypePrivate &) override; + MimeTypePrivate::LocaleHash localeComments(const QString &name) override; + bool hasGlobDeleteAll(const QString &name) override; + QStringList globPatterns(const QString &name) override; + QString icon(const QString &name) override; + QString genericIcon(const QString &name) override; void ensureLoaded() override; void excludeMimeTypeGlobs(const QStringList &toExclude) override; @@ -137,9 +142,10 @@ private: const QString &fileName, qsizetype charPos, bool caseSensitiveCheck); - bool matchMagicRule(CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data); bool isMimeTypeGlobsExcluded(const char *name); - QLatin1StringView iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime); + bool matchMagicRule(CacheFile *cacheFile, int numMatchlets, int firstOffset, + const QByteArray &data); + QLatin1StringView iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime); void loadMimeTypeList(); bool checkCacheChanged(); @@ -149,11 +155,14 @@ private: bool m_mimetypeListLoaded; struct MimeTypeExtra { - // Both retrieved on demand in loadMimeTypePrivate QHash localeComments; QStringList globPatterns; + bool hasGlobDeleteAll = false; }; - QMap m_mimetypeExtra; + using MimeTypeExtraMap = QMap; + MimeTypeExtraMap m_mimetypeExtra; + + MimeTypeExtraMap::const_iterator loadMimeTypeExtra(const QString &mimeName); }; /* @@ -176,19 +185,24 @@ public: bool isValid() override; bool isInternalDatabase() const override; - MimeType mimeTypeForName(const QString &name) override; + bool knowsMimeType(const QString &name) override; void addFileNameMatches(const QString &fileName, MimeGlobMatchResult &result) override; void addParents(const QString &mime, QStringList &result) override; QString resolveAlias(const QString &name) override; void addAliases(const QString &name, QStringList &result) override; - void findByMagic(const QByteArray &data, int *accuracyPtr, MimeType &candidate) override; + void findByMagic(const QByteArray &data, int *accuracyPtr, QString *candidate) override; void addAllMimeTypes(QList &result) override; void ensureLoaded() override; + MimeTypePrivate::LocaleHash localeComments(const QString &name) override; + bool hasGlobDeleteAll(const QString &name) override; + QStringList globPatterns(const QString &name) override; + QString icon(const QString &name) override; + QString genericIcon(const QString &name) override; bool load(const QString &fileName, QString *errorMessage); // Called by the mimetype xml parser - void addMimeType(const MimeType &mt); + void addMimeType(const MimeTypeXMLData &mt); void excludeMimeTypeGlobs(const QStringList &toExclude) override; void addGlobPattern(const MimeGlobPattern &glob); void addParent(const QString &child, const QString &parent); @@ -207,7 +221,7 @@ private: void load(const QString &fileName); void load(const char *data, qsizetype len); - typedef QHash NameMimeTypeMap; + typedef QHash NameMimeTypeMap; NameMimeTypeMap m_nameMimeTypeMap; typedef QHash AliasHash; diff --git a/src/libs/utils/mimetypes2/mimetype.cpp b/src/libs/utils/mimetypes2/mimetype.cpp index 9dbb5e2c66a..f4d88a94df1 100644 --- a/src/libs/utils/mimetypes2/mimetype.cpp +++ b/src/libs/utils/mimetypes2/mimetype.cpp @@ -6,9 +6,6 @@ #include "mimetype_p.h" #include "mimedatabase_p.h" -#include "mimeprovider_p.h" - -#include "mimeglobpattern_p.h" #include #include @@ -20,7 +17,7 @@ using namespace Qt::StringLiterals; namespace Utils { -static QString suffixFromPattern(const QString &pattern) +QString MimeType::suffixFromPattern(const QString &pattern) { // Not a simple suffix if it looks like: README or *. or *.* or *.JP*G or *.JP? if (pattern.startsWith("*."_L1) && @@ -31,33 +28,6 @@ static QString suffixFromPattern(const QString &pattern) return {}; } -MimeTypePrivate::MimeTypePrivate() - : loaded(false), fromCache(false) -{} - -MimeTypePrivate::MimeTypePrivate(const MimeType &other) - : loaded(other.d->loaded), - name(other.d->name), - localeComments(other.d->localeComments), - genericIconName(other.d->genericIconName), - iconName(other.d->iconName), - globPatterns(other.d->globPatterns) -{} - -void MimeTypePrivate::clear() -{ - name.clear(); - localeComments.clear(); - genericIconName.clear(); - iconName.clear(); - globPatterns.clear(); -} - -void MimeTypePrivate::addGlobPattern(const QString &pattern) -{ - globPatterns.append(pattern); -} - /*! \class MimeType \inmodule QtCore @@ -230,7 +200,7 @@ QString MimeType::name() const */ QString MimeType::comment() const { - MimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast(*d)); + const auto localeComments = MimeDatabasePrivate::instance()->localeComments(d->name); QStringList languageList = QLocale().uiLanguages(); qsizetype defaultIndex = languageList.indexOf(u"en-US"_s); @@ -255,13 +225,13 @@ QString MimeType::comment() const // uiLanguages() uses '-' as separator, MIME database uses '_' const QString lang = language == "C"_L1 ? u"en_US"_s : QString(language).replace(u'-', u'_'); - QString comm = d->localeComments.value(lang); + QString comm = localeComments.value(lang); if (!comm.isEmpty()) return comm; const qsizetype cut = lang.indexOf(u'_'); // If "de_CH" is missing, check for "de" (and similar): if (cut != -1) { - comm = d->localeComments.value(lang.left(cut)); + comm = localeComments.value(lang.left(cut)); if (!comm.isEmpty()) return comm; } @@ -287,8 +257,8 @@ QString MimeType::comment() const */ QString MimeType::genericIconName() const { - MimeDatabasePrivate::instance()->loadGenericIcon(const_cast(*d)); - if (d->genericIconName.isEmpty()) { + QString genericIconName = MimeDatabasePrivate::instance()->genericIcon(d->name); + if (genericIconName.isEmpty()) { // From the spec: // If the generic icon name is empty (not specified by the mimetype definition) // then the mimetype is used to generate the generic icon by using the top-level @@ -301,7 +271,7 @@ QString MimeType::genericIconName() const groupRef = groupRef.left(slashindex); return groupRef + "-x-generic"_L1; } - return d->genericIconName; + return genericIconName; } static QString make_default_icon_name_from_mimetype_name(QString iconName) @@ -323,11 +293,11 @@ static QString make_default_icon_name_from_mimetype_name(QString iconName) */ QString MimeType::iconName() const { - MimeDatabasePrivate::instance()->loadIcon(const_cast(*d)); - if (d->iconName.isEmpty()) { + QString iconName = MimeDatabasePrivate::instance()->icon(d->name); + if (iconName.isEmpty()) { return make_default_icon_name_from_mimetype_name(name()); } - return d->iconName; + return iconName; } /*! @@ -339,8 +309,7 @@ QString MimeType::iconName() const */ QStringList MimeType::globPatterns() const { - MimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast(*d)); - return d->globPatterns; + return MimeDatabasePrivate::instance()->globPatterns(d->name); } /*! @@ -434,10 +403,11 @@ QStringList MimeType::aliases() const */ QStringList MimeType::suffixes() const { - MimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast(*d)); + const QStringList patterns = globPatterns(); QStringList result; - for (const QString &pattern : std::as_const(d->globPatterns)) { + result.reserve(patterns.size()); + for (const QString &pattern : patterns) { const QString suffix = suffixFromPattern(pattern); if (!suffix.isEmpty()) result.append(suffix); @@ -473,15 +443,15 @@ QString MimeType::preferredSuffix() const */ QString MimeType::filterString() const { - MimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast(*d)); + const QStringList patterns = globPatterns(); QString filter; - if (!d->globPatterns.empty()) { + if (!patterns.empty()) { filter += comment() + " ("_L1; - for (int i = 0; i < d->globPatterns.size(); ++i) { + for (int i = 0; i < patterns.size(); ++i) { if (i != 0) filter += u' '; - filter += d->globPatterns.at(i); + filter += patterns.at(i); } filter += u')'; } @@ -522,16 +492,7 @@ bool MimeType::matchesName(const QString &nameOrAlias) const */ void MimeType::setPreferredSuffix(const QString &suffix) { - MimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast(*d)); - - auto it = std::find_if(d->globPatterns.begin(), - d->globPatterns.end(), - [suffix](const QString &pattern) { - return suffixFromPattern(pattern) == suffix; - }); - if (it != d->globPatterns.end()) - d->globPatterns.erase(it); - d->globPatterns.prepend(QLatin1String("*.") + suffix); + MimeDatabasePrivate::instance()->setPreferredSuffix(d->name, suffix); } QDebug operator<<(QDebug debug, const Utils::MimeType &mime) diff --git a/src/libs/utils/mimetypes2/mimetype.h b/src/libs/utils/mimetypes2/mimetype.h index b512aaae723..6712134fb67 100644 --- a/src/libs/utils/mimetypes2/mimetype.h +++ b/src/libs/utils/mimetypes2/mimetype.h @@ -82,6 +82,7 @@ public: // Qt Creator additions bool matchesName(const QString &nameOrAlias) const; void setPreferredSuffix(const QString &suffix); + static QString suffixFromPattern(const QString &pattern); protected: friend class MimeTypeParserBase; diff --git a/src/libs/utils/mimetypes2/mimetype_p.h b/src/libs/utils/mimetypes2/mimetype_p.h index 3b8d08df002..3cd56a95f38 100644 --- a/src/libs/utils/mimetypes2/mimetype_p.h +++ b/src/libs/utils/mimetypes2/mimetype_p.h @@ -14,54 +14,24 @@ // We mean it. // -#include "mimetype.h" +#include #include #include namespace Utils { +class MimeBinaryProvider; + class MimeTypePrivate : public QSharedData { public: typedef QHash LocaleHash; - MimeTypePrivate(); - explicit MimeTypePrivate(const MimeType &other); + MimeTypePrivate() { } + explicit MimeTypePrivate(const QString &name) : name(name) { } - void clear(); - - void addGlobPattern(const QString &pattern); - - bool loaded; // QSharedData leaves a 4 byte gap, so don't put 8 byte members first - bool fromCache; // true if this comes from the binary provider - bool hasGlobDeleteAll = false; // true if the mimetype has a glob-deleteall tag QString name; - LocaleHash localeComments; - QString genericIconName; - QString iconName; - QStringList globPatterns; }; } // namespace Utils - -#if 0 -#define QMIMETYPE_BUILDER_FROM_RVALUE_REFS \ - QT_BEGIN_NAMESPACE \ - static QMimeType buildQMimeType ( \ - QString &&name, \ - QString &&genericIconName, \ - QString &&iconName, \ - QStringList &&globPatterns \ - ) \ - { \ - QMimeTypePrivate qMimeTypeData; \ - qMimeTypeData.loaded = true; \ - qMimeTypeData.name = std::move(name); \ - qMimeTypeData.genericIconName = std::move(genericIconName); \ - qMimeTypeData.iconName = std::move(iconName); \ - qMimeTypeData.globPatterns = std::move(globPatterns); \ - return QMimeType(qMimeTypeData); \ - } \ - QT_END_NAMESPACE -#endif diff --git a/src/libs/utils/mimetypes2/mimetypeparser.cpp b/src/libs/utils/mimetypes2/mimetypeparser.cpp index ce7f1caa86c..bb476651d7b 100644 --- a/src/libs/utils/mimetypes2/mimetypeparser.cpp +++ b/src/libs/utils/mimetypes2/mimetypeparser.cpp @@ -163,8 +163,7 @@ static CreateMagicMatchRuleResult createMagicMatchRule(const QXmlStreamAttribute bool MimeTypeParserBase::parse(QIODevice *dev, const QString &fileName, QString *errorMessage) { #if QT_CONFIG(xmlstreamreader) - MimeTypePrivate data; - data.loaded = true; + MimeTypeXMLData data; int priority = 50; QStack currentRules; // stack for the nesting of rules QList rules; // toplevel rules @@ -271,7 +270,7 @@ bool MimeTypeParserBase::parse(QIODevice *dev, const QString &fileName, QString { const auto elementName = reader.name(); if (elementName == QLatin1StringView(mimeTypeTagC)) { - if (!process(MimeType(data), errorMessage)) + if (!process(data, errorMessage)) return false; data.clear(); } else if (elementName == QLatin1StringView(matchTagC)) { @@ -312,4 +311,19 @@ bool MimeTypeParserBase::parse(QIODevice *dev, const QString &fileName, QString #endif // feature xmlstreamreader } +void MimeTypeXMLData::clear() +{ + hasGlobDeleteAll = false; + name.clear(); + localeComments.clear(); + genericIconName.clear(); + iconName.clear(); + globPatterns.clear(); +} + +void MimeTypeXMLData::addGlobPattern(const QString &pattern) +{ + globPatterns.append(pattern); +} + } // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimetypeparser_p.h b/src/libs/utils/mimetypes2/mimetypeparser_p.h index 14477193274..d5aa0cff035 100644 --- a/src/libs/utils/mimetypes2/mimetypeparser_p.h +++ b/src/libs/utils/mimetypes2/mimetypeparser_p.h @@ -14,7 +14,7 @@ // We mean it. // -#include "mimedatabase_p.h" +#include #include "mimeprovider_p.h" @@ -24,6 +24,21 @@ QT_END_NAMESPACE namespace Utils { +class MimeTypeXMLData +{ +public: + void clear(); + + void addGlobPattern(const QString &pattern); + + bool hasGlobDeleteAll = false; // true if the mimetype has a glob-deleteall tag + QString name; + MimeTypePrivate::LocaleHash localeComments; + QString genericIconName; // TODO move to a struct that's specific to the XML provider + QString iconName; // TODO move to a struct that's specific to the XML provider + QStringList globPatterns; +}; + class MimeTypeParserBase { Q_DISABLE_COPY_MOVE(MimeTypeParserBase) @@ -37,7 +52,7 @@ public: static bool parseNumber(QStringView n, int *target, QString *errorMessage); protected: - virtual bool process(const MimeType &t, QString *errorMessage) = 0; + virtual bool process(const MimeTypeXMLData &t, QString *errorMessage) = 0; virtual bool process(const MimeGlobPattern &t, QString *errorMessage) = 0; virtual void processParent(const QString &child, const QString &parent) = 0; virtual void processAlias(const QString &alias, const QString &name) = 0; @@ -71,7 +86,7 @@ public: explicit MimeTypeParser(MimeXMLProvider &provider) : m_provider(provider) {} protected: - inline bool process(const MimeType &t, QString *) override + inline bool process(const MimeTypeXMLData &t, QString *) override { m_provider.addMimeType(t); return true; } inline bool process(const MimeGlobPattern &glob, QString *) override