File indexing completed on 2024-04-28 07:45:14
0001 /* 0002 SPDX-FileCopyrightText: 2018 Dan Leinir Turthra Jensen <admin@leinir.dk> 0003 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "tagsfilterchecker.h" 0008 0009 #include <knewstuffcore_debug.h> 0010 0011 #include <QMap> 0012 0013 namespace KNSCore 0014 { 0015 class TagsFilterCheckerPrivate 0016 { 0017 public: 0018 TagsFilterCheckerPrivate() 0019 { 0020 } 0021 ~TagsFilterCheckerPrivate() 0022 { 0023 qDeleteAll(validators); 0024 } 0025 class Validator; 0026 // If people start using a LOT of validators (>20ish), we can always change it, but 0027 // for now it seems reasonable that QMap is better than QHash here... 0028 QMap<QString, Validator *> validators; 0029 0030 class Validator 0031 { 0032 public: 0033 Validator(const QString &tag, const QString &value) 0034 : m_tag(tag) 0035 { 0036 if (!value.isNull()) { 0037 m_acceptedValues << value; 0038 } 0039 } 0040 virtual ~Validator() 0041 { 0042 } 0043 virtual bool filterAccepts(const QString &tag, const QString &value) = 0; 0044 0045 protected: 0046 friend class TagsFilterCheckerPrivate; 0047 QString m_tag; 0048 QStringList m_acceptedValues; 0049 }; 0050 0051 // Will only accept entries which have one of the accepted values set for the tag key 0052 class EqualityValidator : public Validator 0053 { 0054 public: 0055 EqualityValidator(const QString &tag, const QString &value) 0056 : Validator(tag, value) 0057 { 0058 } 0059 ~EqualityValidator() override 0060 { 0061 } 0062 bool filterAccepts(const QString &tag, const QString &value) override 0063 { 0064 bool result = true; 0065 if (tag == m_tag && !m_acceptedValues.contains(value)) { 0066 qCDebug(KNEWSTUFFCORE) << "Item excluded by filter on" << m_tag << "because" << value << "was not included in" << m_acceptedValues; 0067 result = false; 0068 } 0069 return result; 0070 } 0071 }; 0072 0073 // Will only accept entries which have none of the values set for the tag key 0074 class InequalityValidator : public Validator 0075 { 0076 public: 0077 InequalityValidator(const QString &tag, const QString &value) 0078 : Validator(tag, value) 0079 { 0080 } 0081 ~InequalityValidator() override 0082 { 0083 } 0084 bool filterAccepts(const QString &tag, const QString &value) override 0085 { 0086 bool result = true; 0087 if (tag == m_tag && m_acceptedValues.contains(value)) { 0088 qCDebug(KNEWSTUFFCORE) << "Item excluded by filter on" << m_tag << "because" << value << "was included in" << m_acceptedValues; 0089 result = false; 0090 } 0091 return result; 0092 } 0093 }; 0094 0095 void addValidator(const QString &filter) 0096 { 0097 int pos = 0; 0098 if ((pos = filter.indexOf(QLatin1String("=="))) > -1) { 0099 QString tag = filter.left(pos); 0100 QString value = filter.mid(tag.length() + 2); 0101 Validator *val = validators.value(tag, nullptr); 0102 if (!val) { 0103 val = new EqualityValidator(tag, QString()); 0104 validators.insert(tag, val); 0105 } 0106 val->m_acceptedValues << value; 0107 qCDebug(KNEWSTUFFCORE) << "Created EqualityValidator for tag" << tag << "with value" << value; 0108 } else if ((pos = filter.indexOf(QLatin1String("!="))) > -1) { 0109 QString tag = filter.left(pos); 0110 QString value = filter.mid(tag.length() + 2); 0111 Validator *val = validators.value(tag, nullptr); 0112 if (!val) { 0113 val = new InequalityValidator(tag, QString()); 0114 validators.insert(tag, val); 0115 } 0116 val->m_acceptedValues << value; 0117 qCDebug(KNEWSTUFFCORE) << "Created InequalityValidator for tag" << tag << "with value" << value; 0118 } else { 0119 qCDebug(KNEWSTUFFCORE) << "Critical error attempting to create tag filter validators. The filter is defined as" << filter 0120 << "which is not in the accepted formats key==value or key!=value"; 0121 } 0122 } 0123 }; 0124 0125 TagsFilterChecker::TagsFilterChecker(const QStringList &tagFilter) 0126 : d(new TagsFilterCheckerPrivate) 0127 { 0128 for (const QString &filter : tagFilter) { 0129 d->addValidator(filter); 0130 } 0131 } 0132 0133 TagsFilterChecker::~TagsFilterChecker() = default; 0134 0135 bool TagsFilterChecker::filterAccepts(const QStringList &tags) 0136 { 0137 // if any tag in the content matches any of the tag filters, skip this entry 0138 qCDebug(KNEWSTUFFCORE) << "Checking tags list" << tags << "against validators with keys" << d->validators.keys(); 0139 for (const QString &tag : tags) { 0140 if (tag.isEmpty()) { 0141 // This happens when you do a split on an empty string (not an empty list, a list with one empty element... because reasons). 0142 // Also handy for other things, i guess, though, so let's just catch it here. 0143 continue; 0144 } 0145 QStringList current = tag.split(QLatin1Char('=')); 0146 if (current.length() > 2) { 0147 qCDebug(KNEWSTUFFCORE) << "Critical error attempting to filter tags. Entry has tag defined as" << tag 0148 << "which is not in the format \"key=value\" or \"key\"."; 0149 return false; 0150 } else if (current.length() == 1) { 0151 // If the tag is defined simply as a key, we give it the value "1", just to make our filtering work simpler 0152 current << QStringLiteral("1"); 0153 } 0154 QMap<QString, TagsFilterCheckerPrivate::Validator *>::const_iterator i = d->validators.constBegin(); 0155 while (i != d->validators.constEnd()) { 0156 if (!i.value()->filterAccepts(current.at(0), current.at(1))) { 0157 return false; 0158 } 0159 ++i; 0160 } 0161 } 0162 // If we have arrived here, nothing has filtered the entry 0163 // out (by being either incorrectly tagged or a filter rejecting 0164 // it), and consequently it is an acceptable entry. 0165 return true; 0166 } 0167 0168 }