File indexing completed on 2025-01-19 07:29:17
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 }