summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMikaël Capelle <capelle.mikael@gmail.com>2020-06-08 18:37:05 +0200
committerMikaël Capelle <capelle.mikael@gmail.com>2020-06-08 18:37:05 +0200
commit13d79f3cbe35e233e575d236b8678db8c5e2d62b (patch)
tree297e1241cff50eef41dd627888fef9b359174087 /src
parent4e0d47868ab78c58afca0733d3686c7dc3c6a4d9 (diff)
Add findFiles overload with glob patterns.
Diffstat (limited to 'src')
-rw-r--r--src/glob_matching.h204
-rw-r--r--src/organizercore.cpp7
-rw-r--r--src/organizerproxy.cpp17
-rw-r--r--src/organizerproxy.h3
4 files changed, 227 insertions, 4 deletions
diff --git a/src/glob_matching.h b/src/glob_matching.h
new file mode 100644
index 00000000..2cec583c
--- /dev/null
+++ b/src/glob_matching.h
@@ -0,0 +1,204 @@
+/* Copyright (C) 2020 G'k
+ * Imported by Holt59
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+#ifndef GLOB_MATCHING_H
+#define GLOB_MATCHING_H
+
+#include <cctype>
+#include <string_view>
+#include <QString>
+
+namespace MOShared {
+
+ /**
+ * Contraints string_traits to allow usage of both standard strings and QString in
+ * GlobPattern.
+ */
+ namespace details {
+
+ template <
+ class CharT,
+ class Traits = std::char_traits<CharT>,
+ class Allocator = std::allocator<CharT>>
+ struct string_traits {
+ using string_type = std::basic_string<CharT, Traits, Allocator>;
+ using string_view = std::basic_string_view<CharT, Traits>;
+
+ static auto tolower(CharT c) { return std::tolower(c); }
+
+ static auto empty(string_view const& view) { return view.empty(); }
+ };
+
+ template <>
+ struct string_traits<QChar> {
+ using string_type = QString;
+ using string_view = QString;
+
+ static auto tolower(QChar const& c) { return c.toLower(); }
+ static auto empty(string_view const& view) { return view.isEmpty(); }
+ };
+
+ }
+
+ /**
+ * @brief Class that provides basic wildcard pattern matching.
+ *
+ * From https://gitlab.com/G_ka/playground/-/commits/master/include/wildcards.hpp
+ *
+ * Custom class because the following alternatives have some issues:
+ * - QRegExp is a tad slow, and we need to convert everything to QString.
+ * - QDir::match is VERY slow. I think it converts the glob pattern to a QRegularExpression and
+ * then use it.
+ * - PatchMatchSpecW (Windows API) is fast but does not support some useful glob pattern (e.g.,
+ * [ab]).
+ *
+ * Advantage of this over the above methods:
+ * - It is fast. Quick testing show that this is faster than PatchMatchSpecW.
+ * - It can be used on most string types (QString, std::string, std::wstring, etc.).
+ */
+ template <
+ class CharT,
+ class Traits = std::char_traits<CharT>,
+ class Allocator = std::allocator<CharT>>
+ class GlobPattern {
+ public:
+
+ using traits = details::string_traits<CharT, Traits, Allocator>;
+
+ using string_type = typename traits::string_type;
+ using string_view_type = typename traits::string_view;
+
+ struct card {
+ // Relying on automatic conversion:
+ static constexpr CharT any = '?';
+ static constexpr CharT any_repeat = '*';
+ static constexpr CharT set_begin = '[';
+ static constexpr CharT set_end = ']';
+ };
+
+ public:
+
+ GlobPattern(string_view_type const& s) : v{ s } { }
+
+ const string_type& native() const { return v; }
+
+ constexpr bool match(
+ string_view_type const& str,
+ bool case_sensitive = false)
+ {
+ // Empty pattern can only match with empty sting
+ if (traits::empty(v))
+ return traits::empty(str);
+
+ auto pat_it = v.begin();
+ auto pat_end = v.end();
+
+ auto str_it = str.begin();
+ auto str_end = str.end();
+
+ auto anyrep_pos_pat = pat_end;
+ auto anyrep_pos_str = str_end;
+
+ auto set_pos_pat = pat_end;
+
+ while (str_it != str_end)
+ {
+ CharT current_pat = 0;
+ CharT current_str = -1;
+ if (pat_it != pat_end)
+ {
+ current_pat = case_sensitive ? *pat_it : traits::tolower(*pat_it);
+ current_str = case_sensitive ? *str_it : traits::tolower(*str_it);
+ }
+ if (pat_it != pat_end && current_pat == card::set_begin)
+ {
+ set_pos_pat = pat_it;
+ pat_it++;
+ }
+ else if (pat_it != pat_end && current_pat == card::set_end)
+ {
+ if (anyrep_pos_pat != pat_end)
+ {
+ set_pos_pat = pat_end;
+ pat_it++;
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+ else if (set_pos_pat != pat_end)
+ {
+ if (current_pat == current_str)
+ {
+ set_pos_pat = pat_end;
+ pat_it = std::find(pat_it, pat_end, card::set_end) + 1;
+ str_it++;
+ }
+ else
+ {
+ if (pat_it == pat_end)
+ {
+ return false;
+ }
+ pat_it++;
+ }
+ }
+ else if (pat_it != pat_end && current_pat == current_str)
+ {
+ pat_it++;
+ str_it++;
+ }
+ else if (pat_it != pat_end && current_pat == card::any)
+ {
+ pat_it++;
+ str_it++;
+ }
+ else if (pat_it != pat_end && current_pat == card::any_repeat)
+ {
+ anyrep_pos_pat = pat_it;
+ anyrep_pos_str = str_it;
+ pat_it++;
+ }
+ else if (anyrep_pos_pat != pat_end)
+ {
+ pat_it = anyrep_pos_pat + 1;
+ str_it = anyrep_pos_str + 1;
+ anyrep_pos_str++;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ while (pat_it != pat_end)
+ {
+ CharT cur = case_sensitive ? *pat_it : traits::tolower(*pat_it);
+ if (cur == card::any_repeat)
+ pat_it++;
+ else
+ break;
+ }
+ return pat_it == pat_end;
+ }
+
+ private:
+ string_type v;
+ };
+
+ template <class CharT, class Traits, class Allocator>
+ GlobPattern(std::basic_string<CharT, Traits, Allocator> const&)
+ -> GlobPattern<CharT, Traits, Allocator>;
+
+ template <class CharT>
+ GlobPattern(CharT const*) -> GlobPattern<CharT>;
+
+ GlobPattern(QString const&) -> GlobPattern<QChar>;
+
+
+} // namespace wildcards
+
+#endif
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index efb98cca..27f54d4b 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -862,9 +862,10 @@ QStringList OrganizerCore::findFiles(
dir = dir->findSubDirectoryRecursive(ToWString(path));
if (dir != nullptr) {
std::vector<FileEntryPtr> files = dir->getFiles();
- foreach (FileEntryPtr file, files) {
- if (filter(ToQString(file->getFullPath()))) {
- result.append(ToQString(file->getFullPath()));
+ for (FileEntryPtr &file: files) {
+ QString fullPath = QString::fromStdWString(file->getFullPath());
+ if (filter(fullPath)) {
+ result.append(fullPath);
}
}
}
diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp
index e67952d9..d728ecad 100644
--- a/src/organizerproxy.cpp
+++ b/src/organizerproxy.cpp
@@ -4,6 +4,7 @@
#include "organizercore.h"
#include "plugincontainer.h"
#include "settings.h"
+#include "glob_matching.h"
#include <QObject>
#include <QApplication>
@@ -217,6 +218,22 @@ QStringList OrganizerProxy::findFiles(const QString &path, const std::function<b
return m_Proxied->findFiles(path, filter);
}
+QStringList OrganizerProxy::findFiles(const QString& path, const QStringList& globFilters) const
+{
+ QList<GlobPattern<QChar>> patterns;
+ for (auto& gfilter : globFilters) {
+ patterns.append(GlobPattern(gfilter));
+ }
+ return findFiles(path, [&patterns](const QString& filename) {
+ for (auto& p : patterns) {
+ if (p.match(filename)) {
+ return true;
+ }
+ }
+ return false;
+ });
+}
+
QStringList OrganizerProxy::getFileOrigins(const QString &fileName) const
{
return m_Proxied->getFileOrigins(fileName);
diff --git a/src/organizerproxy.h b/src/organizerproxy.h
index c8a96ed3..9b31b59d 100644
--- a/src/organizerproxy.h
+++ b/src/organizerproxy.h
@@ -35,7 +35,8 @@ public:
virtual MOBase::IModInterface *installMod(const QString &fileName, const QString &nameSuggestion = QString());
virtual QString resolvePath(const QString &fileName) const;
virtual QStringList listDirectories(const QString &directoryName) const;
- virtual QStringList findFiles(const QString &path, const std::function<bool(const QString &)> &filter) const;
+ virtual QStringList findFiles(const QString &path, const std::function<bool(const QString &)> &filter) const override;
+ virtual QStringList findFiles(const QString& path, const QStringList& globFilters) const override;
virtual QStringList getFileOrigins(const QString &fileName) const;
virtual QList<FileInfo> findFileInfos(const QString &path, const std::function<bool(const FileInfo&)> &filter) const;