Commit 365c1ddb authored by Eike Ziller's avatar Eike Ziller
Browse files

MimeDatabase: Add bestMatch(fileName, listOfMimeTypesToCheck) method.



The use case is Qt Creator's C++ model / project parts, where the files
of the projects are sorted into C/C++/Objective-C++ Header/Source
categories, so we only want to know if a file has one of these 6 mime
types.

Change-Id: Ia600fa34beb8dfd2fb406c04b2f36e6ab6b25730
Reviewed-by: default avatarDavid Schulz <david.schulz@theqtcompany.com>
parent 013e10fd
......@@ -344,6 +344,79 @@ QStringList MimeDatabase::allGlobPatterns()
return patterns;
}
static MimeType mimeForName(const QList<MimeType> &types, const QString &name)
{
foreach (const MimeType &mt, types)
if (mt.matchesName(name))
return mt;
return MimeType();
}
MimeType MimeDatabase::bestMatch(const QString &fileName, const QList<MimeType> &types)
{
// Copied together from mimeTypeForFile(QFileInfo) code path ...
// It would be better to be able to work on a list of mime types directly
// Check for directory. We just ignore the code path for special unix nodes.
if (fileName.endsWith(QLatin1Char('/')))
return mimeForName(types, QLatin1String("inode/directory"));
auto d = MimeDatabasePrivate::instance();
QMutexLocker locker(&d->mutex);
QStringList candidatesByName = d->provider()->bestMatchByFileName(QFileInfo(fileName).fileName(),
types);
if (candidatesByName.count() == 1) {
MimeType mt = mimeForName(types, candidatesByName.first());
if (mt.isValid())
return mt;
candidatesByName.clear();
}
// Extension is unknown, or matches multiple mimetypes.
// Pass 2) Match on content, if we can read the data
QFile file(QFileInfo(fileName).absoluteFilePath());
file.open(QIODevice::ReadOnly);
if (file.isOpen()) {
// Read 16K in one go (QIODEVICE_BUFFERSIZE in qiodevice_p.h).
// This is much faster than seeking back and forth into QIODevice.
const QByteArray data = file.peek(16384);
int magicAccuracy = 0;
MimeType candidateByData;
if (data.isEmpty()) {
magicAccuracy = 100;
candidateByData = mimeForName(types, QLatin1String("application/x-zerosize"));
} else {
candidateByData = d->provider()->bestMatchByMagic(data, types, &magicAccuracy);
if (!candidateByData.isValid()) {
if (isTextFile(data)) {
magicAccuracy = 5;
candidateByData = mimeForName(types, QLatin1String("text/plain"));
}
}
}
// Disambiguate conflicting extensions (if magic matching found something)
if (candidateByData.isValid() && magicAccuracy > 0) {
// "for glob_match in glob_matches:"
// "if glob_match is subclass or equal to sniffed_type, use glob_match"
const QString sniffedMime = candidateByData.name();
foreach (const QString &m, candidatesByName) {
if (d->inherits(m, sniffedMime)) {
// We have magic + pattern pointing to this, so it's a pretty good match
return mimeForName(types, m);
}
}
return candidateByData;
}
}
if (candidatesByName.count() > 1) {
candidatesByName.sort();
return mimeForName(types, candidatesByName.first());
}
return MimeType();
}
/*!
\fn MimeType MimeDatabase::mimeTypeForName(const QString &nameOrAlias) const;
Returns a MIME type for \a nameOrAlias or an invalid one if none found.
......
......@@ -86,6 +86,7 @@ public:
static void addMimeTypes(const QString &fileName);
static QString allFiltersString(QString *allFilesFilter = 0);
static QStringList allGlobPatterns();
static MimeType bestMatch(const QString &fileName, const QList<MimeType> &types);
private:
Internal::MimeDatabasePrivate *d;
......
......@@ -719,6 +719,38 @@ QStringList MimeXMLProvider::findByFileName(const QString &fileName, QString *fo
return matchingMimeTypes;
}
QStringList MimeXMLProvider::bestMatchByFileName(const QString &fileName, const QList<MimeType> &types)
{
ensureLoaded();
// this is slow :(
// this would be much better if MimeType had references to their globs & magics
MimeAllGlobPatterns globs;
// fast patterns are fast (hash lookup), no need to reduce that set
globs.m_fastPatterns = m_mimeTypeGlobs.m_fastPatterns;
// fill highWeight and lowWeight glob lists
QSet<QString> names;
foreach (const MimeType &mt, types)
names.insert(mt.name());
foreach (const MimeGlobPattern &pattern, m_mimeTypeGlobs.m_highWeightGlobs) {
if (names.contains(pattern.mimeType()))
globs.m_highWeightGlobs.append(pattern);
}
foreach (const MimeGlobPattern &pattern, m_mimeTypeGlobs.m_lowWeightGlobs) {
if (names.contains(pattern.mimeType()))
globs.m_lowWeightGlobs.append(pattern);
}
QString foundSuffix;
const QStringList matchingMimeTypes = globs.matchingGlobs(fileName, &foundSuffix);
// result can still contain types that are not in our list, because of the fast patterns
QStringList result;
foreach (const QString &match, matchingMimeTypes) {
if (names.contains(match))
result.append(match);
}
return result;
}
MimeType MimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr)
{
ensureLoaded();
......@@ -737,6 +769,30 @@ MimeType MimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr)
return mimeTypeForName(candidate);
}
MimeType MimeXMLProvider::bestMatchByMagic(const QByteArray &data, const QList<MimeType> &types, int *accuracyPtr)
{
ensureLoaded();
QSet<QString> names;
foreach (const MimeType &mt, types)
names.insert(mt.name());
QString candidate;
foreach (const MimeMagicRuleMatcher &matcher, m_magicMatchers) {
if (!names.contains(matcher.mimetype()))
continue;
if (matcher.matches(data)) {
const int priority = matcher.priority();
if (priority > *accuracyPtr) {
*accuracyPtr = priority;
candidate = matcher.mimetype();
}
}
}
return mimeTypeForName(candidate);
}
void MimeXMLProvider::ensureLoaded()
{
if (!m_loaded /*|| shouldCheck()*/) {
......
......@@ -73,6 +73,10 @@ public:
virtual void loadIcon(MimeTypePrivate &) {}
virtual void loadGenericIcon(MimeTypePrivate &) {}
// Qt Creator additions
virtual QStringList bestMatchByFileName(const QString &fileName, const QList<MimeType> &types) = 0;
virtual MimeType bestMatchByMagic(const QByteArray &data, const QList<MimeType> &types, int *accuracyPtr) = 0;
MimeDatabasePrivate *m_db;
protected:
bool shouldCheck();
......@@ -150,6 +154,8 @@ public:
// Qt Creator additions
void addFile(const QString &filePath);
QStringList bestMatchByFileName(const QString &fileName, const QList<MimeType> &types);
MimeType bestMatchByMagic(const QByteArray &data, const QList<MimeType> &types, int *accuracyPtr);
private:
void ensureLoaded();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment