diff options
| author | Mikaël Capelle <capelle.mikael@gmail.com> | 2020-06-08 18:37:05 +0200 |
|---|---|---|
| committer | Mikaël Capelle <capelle.mikael@gmail.com> | 2020-06-08 18:37:05 +0200 |
| commit | 13d79f3cbe35e233e575d236b8678db8c5e2d62b (patch) | |
| tree | 297e1241cff50eef41dd627888fef9b359174087 /src | |
| parent | 4e0d47868ab78c58afca0733d3686c7dc3c6a4d9 (diff) | |
Add findFiles overload with glob patterns.
Diffstat (limited to 'src')
| -rw-r--r-- | src/glob_matching.h | 204 | ||||
| -rw-r--r-- | src/organizercore.cpp | 7 | ||||
| -rw-r--r-- | src/organizerproxy.cpp | 17 | ||||
| -rw-r--r-- | src/organizerproxy.h | 3 |
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;
|
