#pragma once #include #include #include #include // Base interface for line sorting. class ISorter { private: bool _isDescending = true; size_t _fromColumn = 0; size_t _toColumn = 0; protected: bool isDescending() const { return _isDescending; } QString getSortKey(const QString& input) { if (isSortingSpecificColumns()) { if (input.length() < _fromColumn) { // prevent an std::out_of_range exception return QString(""); } else if (_fromColumn == _toColumn) { // get characters from the indicated column to the end of the line return input.mid(_fromColumn); } else { // get characters between the indicated columns, inclusive return input.mid(_fromColumn, _toColumn - _fromColumn); } } else { return input; } } bool isSortingSpecificColumns() { return _toColumn != 0; } public: ISorter(bool isDescending, size_t fromColumn, size_t toColumn) : _isDescending(isDescending), _fromColumn(fromColumn), _toColumn(toColumn) { assert(_fromColumn <= _toColumn); }; virtual ~ISorter() { }; virtual QList sort(QList lines) = 0; }; // Implementation of lexicographic sorting of lines. class LexicographicSorter : public ISorter { public: LexicographicSorter(bool isDescending, size_t fromColumn, size_t toColumn) : ISorter(isDescending, fromColumn, toColumn) { }; QList sort(QList lines) override { // Note that both branches here are equivalent in the sense that they always give the same answer. // However, if we are *not* sorting specific columns, then we get a 40% speed improvement by not calling // getSortKey() so many times. if (isSortingSpecificColumns()) { std::sort(lines.begin(), lines.end(), [this](QString a, QString b) { if (isDescending()) { return getSortKey(a).compare(getSortKey(b)) > 0; } else { return getSortKey(a).compare(getSortKey(b)) < 0; } }); } else { std::sort(lines.begin(), lines.end(), [this](QString a, QString b) { if (isDescending()) { return a.compare(b) > 0; } else { return a.compare(b) < 0; } }); } return lines; } }; // Implementation of lexicographic sorting of lines, ignoring character casing class LexicographicCaseInsensitiveSorter : public ISorter { public: LexicographicCaseInsensitiveSorter(bool isDescending, size_t fromColumn, size_t toColumn) : ISorter(isDescending, fromColumn, toColumn) { }; QList sort(QList lines) override { // Note that both branches here are equivalent in the sense that they always give the same answer. // However, if we are *not* sorting specific columns, then we get a 40% speed improvement by not calling // getSortKey() so many times. if (isSortingSpecificColumns()) { std::sort(lines.begin(), lines.end(), [this](QString a, QString b) { if (isDescending()) { return getSortKey(a).compare(getSortKey(b), Qt::CaseInsensitive) > 0; } else { return getSortKey(a).compare(getSortKey(b), Qt::CaseInsensitive) < 0; } }); } else { std::sort(lines.begin(), lines.end(), [this](QString a, QString b) { if (isDescending()) { return QString::compare(a, b, Qt::CaseInsensitive) > 0; } else { return QString::compare(a, b, Qt::CaseInsensitive) < 0; } }); } return lines; } }; class ReverseSorter : public ISorter { public: ReverseSorter(bool isDescending, size_t fromColumn, size_t toColumn) : ISorter(isDescending, fromColumn, toColumn) { }; QList sort(QList lines) override { std::reverse(lines.begin(), lines.end()); return lines; } };