From 44efb910a56b8c46072c63c04a406b985f16352a Mon Sep 17 00:00:00 2001 From: Hamatoma Date: Wed, 23 Nov 2016 00:26:53 +0100 Subject: [PATCH] ReSearch: filter implemented --- appl/research/filecache.cpp | 97 ++++++++++++++++++++++++++++++++ appl/research/filecache.hpp | 29 ++++++++++ appl/research/filefilter.cpp | 106 +++++++++++++++++++++++++++++++++++ appl/research/filefilter.hpp | 28 +++++++++ appl/research/filefinder.cpp | 63 +++++++++++++++++---- appl/research/filefinder.hpp | 14 ++++- appl/research/mainwindow.cpp | 67 +++++++++++++++++++++- appl/research/mainwindow.hpp | 18 +++--- appl/research/mainwindow.ui | 61 ++++++++++++++++++-- appl/research/research.hpp | 11 ++++ appl/research/research.pro | 8 ++- base/ReFileSearch.cpp | 27 ++++++++- base/ReFileSearch.hpp | 10 +++- base/ReQStringUtils.hpp | 4 +- 14 files changed, 505 insertions(+), 38 deletions(-) create mode 100644 appl/research/filecache.cpp create mode 100644 appl/research/filecache.hpp create mode 100644 appl/research/filefilter.cpp create mode 100644 appl/research/filefilter.hpp diff --git a/appl/research/filecache.cpp b/appl/research/filecache.cpp new file mode 100644 index 0000000..b1416f3 --- /dev/null +++ b/appl/research/filecache.cpp @@ -0,0 +1,97 @@ +#include "research.hpp" +#include +/** + * Constructor. + * + * @param filename filename with path + */ +FileCacheItem::FileCacheItem(const QString &filename) : + m_lines(), + m_filename(filename), + m_lastModified() +{ + read(); +} + +/** + * Reads the content of the file into the line list. + */ +void FileCacheItem::read() +{ + QFileInfo info(m_filename); + m_lastModified = info.lastModified(); + QFile file(m_filename); + file.open(QFile::ReadOnly); + m_lines.clear(); + QTextStream textStream(&file); + QString line; + while (true) + { + line = textStream.readLine(); + if (line.isNull()) + break; + else + m_lines.append(line); + } +} + +/** + * Tests whether the file content has changed. + * If changed the file is read. + */ +void FileCacheItem::update() +{ + QFileInfo info(m_filename); + if (m_lastModified != info.lastModified()){ + read(); + } +} + +/** + * Returns the lines of the file. + * + * @return the list of lines of the file + */ +const QStringList& FileCacheItem::lines() const +{ + return m_lines; +} + +/** + * Constructor. + */ +FileCache::FileCache() : + m_cache(), + m_emptyLines() +{ + +} + +/** + * Adds a file to the file cache. + * + * @param filename + */ +void FileCache::addOrUpdate(const QString &filename) +{ + if (m_cache.contains(filename)) { + m_cache.value(filename)->update(); + } else { + m_cache.insert(filename, new FileCacheItem(filename)); + } + +} + +/** + * Returns the line list of a given file. + * + * @param filename filename with path + * @return the list of lines of the files + */ +const QStringList &FileCache::lines(const QString &filename) const +{ + FileCacheItem* item = m_cache.value(filename, NULL); + return item == NULL ? m_emptyLines : item->lines(); +} + + diff --git a/appl/research/filecache.hpp b/appl/research/filecache.hpp new file mode 100644 index 0000000..33c87ce --- /dev/null +++ b/appl/research/filecache.hpp @@ -0,0 +1,29 @@ +#ifndef FILECACHE_H +#define FILECACHE_H + +class FileCacheItem { +public: + FileCacheItem(const QString& filename); +public: + const QStringList& lines() const; + void read(); + void update(); +private: + QStringList m_lines; + QString m_filename; + QDateTime m_lastModified; +}; + +class FileCache +{ +public: + FileCache(); +public: + void addOrUpdate(const QString& filename); + const QStringList& lines(const QString& filename) const; +private: + QMap m_cache; + QStringList m_emptyLines; +}; + +#endif // FILECACHE_H diff --git a/appl/research/filefilter.cpp b/appl/research/filefilter.cpp new file mode 100644 index 0000000..0a2f3c1 --- /dev/null +++ b/appl/research/filefilter.cpp @@ -0,0 +1,106 @@ +#include "research.hpp" + +/** + * Construcctor. + * + * @param table table containing the files + * @param list list widget to store the filtered lines + */ +FileFilter::FileFilter(QTableWidget &table, QListWidget &list, FileCache& cache) : + m_table(table), + m_list(list), + m_cache(cache), + m_includeExpr(), + m_excludeExpr(), + m_caseSensitive(false), + m_fromHit(0), + m_toHit(0), + m_linesAbove(0), + m_linesBelow(0) + +{ + +} + +/** + * Puts all filtered lines from the files in the table into the list. + * + * @param includePattern regular expression to find lines to filter + * @param excludePattern regular expression to prevent lines from filtering + * @param fromHit first hit which is visible in the list + * @param toHit the filtering stops behind this hit + * @param linesAbove number of lines above the hit which will put into the list + * @param linesBelow number of lines below the hit which will put into the list + */ +void FileFilter::filter(const QString &includePattern, + const QString &excludePattern, + int fromHit, int toHit, + int linesAbove, int linesBelow, + bool caseSensitive) +{ + int ixFile = -1; + int hitNo = 0; + int fileCount = m_table.rowCount(); + m_list.clear(); + m_fromHit = fromHit; + m_toHit = toHit; + m_linesAbove = linesAbove; + m_linesBelow = linesBelow; + delete m_includeExpr; + delete m_excludeExpr; + + QRegularExpression::PatternOption option = caseSensitive + ? QRegularExpression::NoPatternOption + : QRegularExpression::CaseInsensitiveOption; + m_includeExpr = includePattern.isEmpty() ? NULL : new QRegularExpression(includePattern, option); + m_excludeExpr = excludePattern.isEmpty() ? NULL : new QRegularExpression(excludePattern, option); + + while (ixFile < fileCount - 1 && hitNo <= toHit){ + ++ixFile; + QString full = m_table.item(ixFile, colPath)->text(); + full += OS_SEPARATOR_STR; + full += m_table.item(ixFile, colNode)->text(); + + hitNo = filterOneFile(full, hitNo); + } +} + +/** + * Filters the lines of one file. + * + * @param filename filename with path + * @param lastHit the last found hit in the previous files + * @return the number of hits including the hits in the file + */ +int FileFilter::filterOneFile(const QString& filename, int lastHit) +{ + m_cache.addOrUpdate(filename); + const QStringList& lines = m_cache.lines(filename); + + int lineNo = 0; + int lastLine = -1; + int count = lines.size(); + QString prefixHit = filename + "-"; + QString prefixOther = prefixHit; + if (m_linesAbove + m_linesBelow > 0){ + prefixHit = ">" + prefixHit; + prefixOther = " " + prefixOther; + } + while(lastHit <= m_toHit && lineNo < count){ + QString line = lines[lineNo++]; + if (m_includeExpr != NULL && ! m_includeExpr->match(line).hasMatch()) + continue; + if (m_excludeExpr != NULL && m_excludeExpr->match(line).hasMatch()) + continue; + if (++lastHit >= m_fromHit){ + for (int ix = max(lastLine + 1, max(0, lineNo - m_linesAbove)); + ix <= min(lineNo + m_linesBelow, count - 1); + ix++){ + m_list.addItem((ix == lineNo ? prefixHit : prefixOther) + + QString::number(ix + 1) + ": " + lines[ix]); + } + } + } + return lastHit; +} + diff --git a/appl/research/filefilter.hpp b/appl/research/filefilter.hpp new file mode 100644 index 0000000..2cf113c --- /dev/null +++ b/appl/research/filefilter.hpp @@ -0,0 +1,28 @@ +#ifndef FILEFILTER_H +#define FILEFILTER_H + + +class FileFilter +{ +public: + FileFilter(QTableWidget& table, QListWidget& list, FileCache& cache); +public: + void filter(const QString& includePattern, const QString& excludePattern, + int fromHit, int toHit, int linesAbove, int linesBelow, + bool caseSensitive); +private: + int filterOneFile(const QString &filename, int lastHit); +private: + QTableWidget& m_table; + QListWidget& m_list; + FileCache& m_cache; + QRegularExpression* m_includeExpr; + QRegularExpression* m_excludeExpr; + bool m_caseSensitive; + int m_fromHit; + int m_toHit; + int m_linesAbove; + int m_linesBelow; +}; + +#endif // FILEFILTER_H diff --git a/appl/research/filefinder.cpp b/appl/research/filefinder.cpp index 552d607..8db0a29 100644 --- a/appl/research/filefinder.cpp +++ b/appl/research/filefinder.cpp @@ -1,10 +1,5 @@ #include "research.hpp" -const int IX_NODE = 0; -const int IX_SIZE = 1; -const int IX_DATE = 2; -const int IX_PATH = 3; - /** * Constructor. @@ -14,11 +9,22 @@ const int IX_PATH = 3; FileFinder::FileFinder(QTableWidget& table) : ReFileSearch(), m_table(table), - m_files() + m_files(), + m_dirs(0), + m_foundFiles(0), + m_ignoredFiles(0) { setSearchMode(smFiles); } +/** + * Destructor. + */ +FileFinder::~FileFinder() +{ + +} + /** * Adds a file to the file table. * @@ -30,18 +36,51 @@ FileFinder::FileFinder(QTableWidget& table) : void FileFinder::addToTable(const QString& full, const QString &path, const QString &node, const QFileInfo& info) { - if (! m_files.contains(full)){ + if (m_files.contains(full)){ + ++m_ignoredFiles; + } else { + ++m_foundFiles; int last = m_table.rowCount(); m_files.insert(full); m_table.setRowCount(last + 1); - m_table.setItem(last, IX_NODE, new QTableWidgetItem(node)); - m_table.setItem(last, IX_SIZE, new QTableWidgetItem(QString::number(info.size()))); + m_table.setItem(last, colNode, new QTableWidgetItem(node)); + m_table.setItem(last, colSize, new QTableWidgetItem(QString::number(info.size()))); QString date = info.lastModified().toString("yyyy.MM.dd hh:mm.ss"); - m_table.setItem(last, IX_DATE, new QTableWidgetItem(date)); - m_table.setItem(last, IX_PATH, new QTableWidgetItem(path)); + m_table.setItem(last, colDate, new QTableWidgetItem(date)); + m_table.setItem(last, colPath, new QTableWidgetItem(path)); } } +/** + * Clears the table. + */ +void FileFinder::clear() +{ + m_table.setRowCount(0); + m_files.clear(); +} + +/** + * Resturn the number of found files of the last search. + * + * @return the number of found files + */ +int FileFinder::foundFiles() const +{ + return m_foundFiles; +} + + +/** + * Returns the number of files which are ignored because they are already in the table. + * + * @return the number of ignored files + */ +int FileFinder::ignoredFiles() const +{ + return m_ignoredFiles; +} + /** * Handle a file found by the file search criteria. @@ -75,6 +114,8 @@ void FileFinder::search(const QString &baseDirectory, const QString &patterns, setMinDepth(minDepth); setMaxDepth(maxDepth); setPatterns(patterns); + m_ignoredFiles = 0; + m_foundFiles = 0; oneDirectory(baseDirectory, 0); } diff --git a/appl/research/filefinder.hpp b/appl/research/filefinder.hpp index acfedec..575c721 100644 --- a/appl/research/filefinder.hpp +++ b/appl/research/filefinder.hpp @@ -6,17 +6,25 @@ class FileFinder : public ReFileSearch { public: FileFinder( QTableWidget& m_table); + virtual ~FileFinder(); public: + void addToTable(const QString& full, const QString &path, + const QString &node, const QFileInfo &info); + void clear(); + int foundFiles() const; bool handleFile(const QString &full, const QString &path, const QString &node, const QFileInfo &info); + int ignoredFiles() const; void search(const QString& baseDirectory, const QString& patterns, int minDepth, int maxDepth); -public: - void addToTable(const QString& full, const QString &path, - const QString &node, const QFileInfo &info); + + private: QTableWidget& m_table; QSet m_files; + int m_dirs; + int m_foundFiles; + int m_ignoredFiles; }; #endif // FILEFINDER_H diff --git a/appl/research/mainwindow.cpp b/appl/research/mainwindow.cpp index 8ea0410..337323c 100644 --- a/appl/research/mainwindow.cpp +++ b/appl/research/mainwindow.cpp @@ -12,15 +12,41 @@ MainWindow::MainWindow(QApplication& application, const QString& homeDir, ReGuiApplication(application, "rsearch", homeDir, 2, 10100100, "de", parent), ReGuiValidator(), ui(new Ui::MainWindow), - m_fileFinder(NULL) + m_fileFinder(NULL), + m_fileCache(new FileCache()), + m_test(true) { ReComboBox::setDefaultHistorySize(20); initializeGui(); } +/** + * Destructor. + */ MainWindow::~MainWindow() { delete ui; + delete m_fileCache; + delete m_fileFinder; +} + +/** + * Gets an integer from a combobox. + * + * If the text is not an integer the default value will be used. + * + * @param combobox the source of the integer + * @param defaultValue the value if the source value is invalid + */ +int MainWindow::comboboxToInt(QComboBox& combobox, int defaultValue){ + QString text = combobox.currentText(); + uint rc = 0; + if (ReQStringUtils::lengthOfUInt(text, 0, 10, &rc) != text.length()){ + say(LOG_ERROR, tr("not an integer: %1 using instead: %2").arg(text).arg(defaultValue)); + combobox.setCurrentText(QString::number(defaultValue)); + rc = (uint) defaultValue; + } + return (int) rc; } /** @@ -37,10 +63,21 @@ void MainWindow::initializeGui(){ connect(ui->pushButtonSelectBase, SIGNAL(clicked()), this, SLOT(onSelectBaseDirectory())); connect(ui->pushButtonAdd, SIGNAL(clicked()), this, SLOT(onAdd())); connect(ui->pushButtonClear, SIGNAL(clicked()), this, SLOT(onClear())); + connect(ui->pushButtonFilter, SIGNAL(clicked()), this, SLOT(onFilter())); delete m_fileFinder; m_fileFinder = new FileFinder(*ui->tableWidget); + ui->tableWidget->setColumnWidth(colNode, 200); + ui->tableWidget->setColumnWidth(colSize, 125); + ui->tableWidget->setColumnWidth(colDate, 175); + + if (m_test){ + ui->comboBoxBaseDirectory->setCurrentText("/etc"); + ui->comboBoxFilePatterns->setCurrentText("*asu*"); + ui->comboBoxIncludingPattern->setCurrentText("e"); + onAdd(); + } } /** @@ -70,8 +107,15 @@ void MainWindow::onAdd() say(LOG_ERROR, tr("missing a base directory")); } else { m_fileFinder->search(base, ui->comboBoxFilePatterns->currentText(), - atoi(ui->lineEditMaxDepth->text().toLocal8Bit().data()), - atoi(ui->lineEditMinDepth->text().toLocal8Bit().data())); + atoi(ui->lineEditMinDepth->text().toLocal8Bit().data()), + atoi(ui->lineEditMaxDepth->text().toLocal8Bit().data())); + int files = ui->tableWidget->rowCount(); + ui->labelFileCount->setText(QString::number(files) + + " " + tr("file(s)")); + say(LOG_INFO, tr("files: %1 already found: %2 processed directories: %3") + .arg(m_fileFinder->foundFiles()) + .arg(m_fileFinder->ignoredFiles()) + .arg(m_fileFinder->processedDirs())); } } @@ -80,7 +124,24 @@ void MainWindow::onAdd() */ void MainWindow::onClear() { + m_fileFinder->clear(); + ui->labelFileCount->setText(""); +} +/** + * Handles the click of the "Filter" button. + */ +void MainWindow::onFilter() +{ + FileFilter filter(*ui->tableWidget, *ui->listWidgetHits, *m_fileCache); + int from = comboboxToInt(*ui->comboBoxFromHit, 0); + int to = comboboxToInt(*ui->comboBoxToHit, from + 100); + int above = comboboxToInt(*ui->comboBoxLinesAbove, 0); + int below = comboboxToInt(*ui->comboBoxLinesBelow, 0); + + filter.filter(ui->comboBoxIncludingPattern->currentText(), + ui->comboBoxExcludingPattern->currentText(), + from, to, above, below, false); } /** diff --git a/appl/research/mainwindow.hpp b/appl/research/mainwindow.hpp index 61c1410..9f7f083 100644 --- a/appl/research/mainwindow.hpp +++ b/appl/research/mainwindow.hpp @@ -10,6 +10,7 @@ #include class FileFinder; +class FileCache; namespace Ui { class MainWindow; } @@ -26,19 +27,22 @@ public: virtual bool say(ReLoggerLevel level, const QString& message); private: - void initializeGui(); + int comboboxToInt(QComboBox &combobox, int defaultValue); + void initializeGui(); virtual void onAboutToQuit(); virtual void onGuiTimerUpdate(); virtual void onLanguageChange(); private slots: - void onAbout(); - void onAdd(); - void onClear(); - void onSelectBaseDirectory(); - void textEdited(const QString& oldString, const QString& newString); - + void onAbout(); + void onAdd(); + void onClear(); + void onFilter(); + void onSelectBaseDirectory(); + void textEdited(const QString& oldString, const QString& newString); private: Ui::MainWindow *ui; FileFinder* m_fileFinder; + FileCache* m_fileCache; + bool m_test; }; #endif // MAINWINDOW_H diff --git a/appl/research/mainwindow.ui b/appl/research/mainwindow.ui index b944641..4ca7c7f 100644 --- a/appl/research/mainwindow.ui +++ b/appl/research/mainwindow.ui @@ -229,6 +229,19 @@ &Clear + + + + 0 + 120 + 131 + 20 + + + + + + @@ -270,9 +283,9 @@ - + - Text + Filter @@ -292,7 +305,7 @@ - 1 + 0 @@ -336,9 +349,9 @@ - 175 - 10 - 321 + 11 + 11 + 298 38 @@ -396,6 +409,16 @@ + + + + &Filter + + + true + + + @@ -409,6 +432,32 @@ + + + + + 25 + 16777215 + + + + < + + + + + + + + 25 + 16777215 + + + + > + + + diff --git a/appl/research/research.hpp b/appl/research/research.hpp index 5f92b05..5c94a62 100644 --- a/appl/research/research.hpp +++ b/appl/research/research.hpp @@ -11,4 +11,15 @@ #include "base/rebase.hpp" #include "gui/regui.hpp" #include "filefinder.hpp" +#include "filecache.hpp" +#include "filefilter.hpp" + +enum ColumnFiles { + colNode = 0, + colSize, + colDate, + colPath +}; + + #endif // RESEARCH_HPP diff --git a/appl/research/research.pro b/appl/research/research.pro index 7aaf1ad..5ce9496 100644 --- a/appl/research/research.pro +++ b/appl/research/research.pro @@ -32,7 +32,9 @@ SOURCES += main.cpp \ mainwindow.cpp \ aboutdialog.cpp \ ../../base/ReFileSearch.cpp \ - filefinder.cpp + filefinder.cpp \ + filecache.cpp \ + filefilter.cpp HEADERS += mainwindow.hpp \ ../../base/rebase.hpp \ @@ -45,7 +47,9 @@ HEADERS += mainwindow.hpp \ aboutdialog.hpp \ research.hpp \ ../../base/ReFileSearch.hpp \ - filefinder.hpp + filefinder.hpp \ + filecache.hpp \ + filefilter.hpp FORMS += mainwindow.ui \ aboutdialog.ui diff --git a/base/ReFileSearch.cpp b/base/ReFileSearch.cpp index e2a0928..3fe3264 100644 --- a/base/ReFileSearch.cpp +++ b/base/ReFileSearch.cpp @@ -15,7 +15,15 @@ ReFileSearch::ReFileSearch() : m_minDepth(0), m_maxDepth(9999), m_searchMode(smFilesAndDirs), - m_matcher() + m_matcher(), + m_processedDirs(0) +{ +} + +/** + * Destructor. + */ +ReFileSearch::~ReFileSearch() { } @@ -39,6 +47,14 @@ int ReFileSearch::minDepth() const return m_minDepth; } +/** + * Does the search in one directory. + * + * Note: this method is recursiv. + * + * @param directory the directory to process + * @param depth the depth of the directory (distance from the base) + */ void ReFileSearch::oneDirectory(const QString& directory, int depth) { QDir dir(directory); @@ -47,6 +63,10 @@ void ReFileSearch::oneDirectory(const QString& directory, int depth) QString full; QStringList dirs; bool stop = false; + if (depth == 0) + m_processedDirs = 1; + else + ++m_processedDirs; for (int ix = 0; ! stop && ix < files.size(); ix++){ node = files[ix]; if (node == "." || node == "..") @@ -98,6 +118,11 @@ void ReFileSearch::setSearchMode(const SearchMode& searchMode) m_searchMode = searchMode; } +int ReFileSearch::processedDirs() const +{ + return m_processedDirs; +} + /** * Sets the maximal depth. * diff --git a/base/ReFileSearch.hpp b/base/ReFileSearch.hpp index 75de633..409d886 100644 --- a/base/ReFileSearch.hpp +++ b/base/ReFileSearch.hpp @@ -26,6 +26,7 @@ public: public: ReFileSearch(); + virtual ~ReFileSearch(); public: /** * Handles the found file. @@ -42,16 +43,19 @@ public: int maxDepth() const; int minDepth() const; void oneDirectory(const QString& directory, int depth); + int processedDirs() const; SearchMode searchMode() const; void setMaxDepth(int maxDepth); void setMinDepth(int minDepth); void setPatterns(const QString& includeExcludePattern); void setSearchMode(const SearchMode& searchMode); -public: - int m_minDepth; - int m_maxDepth; + +protected: + int m_minDepth; + int m_maxDepth; SearchMode m_searchMode; ReIncludeExcludeMatcher m_matcher; + int m_processedDirs; }; #endif // REFILESEARCH_HPP diff --git a/base/ReQStringUtils.hpp b/base/ReQStringUtils.hpp index c05b8c5..9c44209 100644 --- a/base/ReQStringUtils.hpp +++ b/base/ReQStringUtils.hpp @@ -69,8 +69,8 @@ public: NULL); static int lengthOfUInt64(const ReString& text, int start = 0, int radix = 10, quint64* value = NULL); - static int lengthOfUInt(const ReString& text, int start, int radix, - uint* pValue); + static int lengthOfUInt(const ReString& text, int start = 0, int radix = 10, + uint* pValue = NULL); static QString longestPrefix(const QStringList& list); static bool match(const QString& heap, const QStringList& needles); /** -- 2.39.5