File indexing completed on 2024-05-05 04:42:42

0001 /*
0002     SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include <QString>
0008 
0009 namespace KPublicTransport {
0010 
0011 // implementation details shared with the line metadata code generator
0012 namespace Internal {
0013 
0014 enum LineNameCompareMode {
0015     StrictCompare,
0016     FuzzyCompare,
0017 };
0018 
0019 template <typename Iter>
0020 inline bool isSameLineName(const Iter &lBegin, const Iter &lEnd, const Iter &rBegin, const Iter &rEnd, LineNameCompareMode mode)
0021 {
0022     auto lIt = lBegin;
0023     auto rIt = rBegin;
0024     while (lIt != lEnd && rIt != rEnd) {
0025         // ignore spaces etc.
0026         if (!(*lIt).isLetterOrNumber()) {
0027             ++lIt;
0028             continue;
0029         }
0030         if (!(*rIt).isLetterOrNumber()) {
0031             ++rIt;
0032             continue;
0033         }
0034 
0035         if ((*lIt).toCaseFolded() != (*rIt).toCaseFolded()) {
0036             return false;
0037         }
0038 
0039         ++lIt;
0040         ++rIt;
0041     }
0042 
0043     if (lIt == lEnd && rIt == rEnd) { // both inputs fully consumed, and no mismatch found
0044         return true;
0045     }
0046 
0047     if (mode == StrictCompare) {
0048         return false;
0049     }
0050     // one input is prefix of the other, that is ok if there's a separator
0051     return (lIt != lEnd && (*lIt).isSpace()) || (rIt != rEnd && (*rIt).isSpace());
0052 }
0053 
0054 inline bool hasMatchingPrefixAndSuffix(QStringView lhs, QStringView rhs)
0055 {
0056     // find matching prefix
0057     int prefixLen = 0;
0058     for (auto lIt = lhs.begin(), rIt = rhs.begin(); lIt != lhs.end() && rIt != rhs.end(); ++lIt, ++rIt) {
0059         if ((*lIt).toCaseFolded() == (*rIt).toCaseFolded()) {
0060             continue;
0061         }
0062         prefixLen = std::distance(lhs.begin(), lIt);
0063         if (!prefixLen || !(*std::prev(lIt)).isSpace()) { // no prefix or no separator
0064             return false;
0065         }
0066         break;
0067     }
0068 
0069     // find matching suffix
0070     int suffixLen = 0;
0071     for (auto lIt = lhs.rbegin(), rIt = rhs.rbegin(); lIt != lhs.rend() && rIt != rhs.rend(); ++lIt, ++rIt) {
0072         if ((*lIt).toCaseFolded() == (*rIt).toCaseFolded()) {
0073             continue;
0074         }
0075         suffixLen = std::distance(lhs.rbegin(), lIt);
0076         if (!suffixLen || !(*std::prev(lIt)).isSpace()) { // no suffix or no separator
0077             return false;
0078         }
0079         break;
0080     }
0081 
0082     const auto fullLen = prefixLen + suffixLen - 1; // -1 for the separator used twice
0083     if (lhs.size() > fullLen && rhs.size() > fullLen) {
0084         return false; // both have a varying middle part
0085     }
0086 
0087     // verify the single sided middle part is not containing numbers
0088     if (lhs.size() > rhs.size()) {
0089         std::swap(lhs, rhs);
0090     }
0091     for (auto i = prefixLen; i < rhs.size() - suffixLen; ++i) {
0092         if (rhs[i].isDigit()) {
0093             return false;
0094         }
0095     }
0096 
0097     return true;
0098 }
0099 
0100 inline bool isSameLineName(const QString &lhs, const QString &rhs, LineNameCompareMode mode)
0101 {
0102     return lhs.compare(rhs, Qt::CaseInsensitive) == 0
0103         || isSameLineName(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), mode)
0104         || isSameLineName(lhs.rbegin(), lhs.rend(), rhs.rbegin(), rhs.rend(), mode)
0105         || (mode == FuzzyCompare && hasMatchingPrefixAndSuffix(lhs, rhs));
0106 }
0107 
0108 }
0109 }