1/*
2 SPDX-FileCopyrightText: 2018 Dan Leinir Turthra Jensen <admin@leinir.dk>
3
4 SPDX-License-Identifier: LGPL-2.1-or-later
5*/
6
7#include "tagsfilterchecker.h"
8
9#include <knewstuffcore_debug.h>
10
11#include <QMap>
12
13namespace KNSCore
14{
15class TagsFilterCheckerPrivate
16{
17public:
18 TagsFilterCheckerPrivate()
19 {
20 }
21 ~TagsFilterCheckerPrivate()
22 {
23 qDeleteAll(c: validators);
24 }
25 class Validator;
26 // If people start using a LOT of validators (>20ish), we can always change it, but
27 // for now it seems reasonable that QMap is better than QHash here...
28 QMap<QString, Validator *> validators;
29
30 class Validator
31 {
32 public:
33 Validator(const QString &tag, const QString &value)
34 : m_tag(tag)
35 {
36 if (!value.isNull()) {
37 m_acceptedValues << value;
38 }
39 }
40 virtual ~Validator()
41 {
42 }
43 virtual bool filterAccepts(const QString &tag, const QString &value) = 0;
44
45 protected:
46 friend class TagsFilterCheckerPrivate;
47 QString m_tag;
48 QStringList m_acceptedValues;
49 };
50
51 // Will only accept entries which have one of the accepted values set for the tag key
52 class EqualityValidator : public Validator
53 {
54 public:
55 EqualityValidator(const QString &tag, const QString &value)
56 : Validator(tag, value)
57 {
58 }
59 ~EqualityValidator() override
60 {
61 }
62 bool filterAccepts(const QString &tag, const QString &value) override
63 {
64 bool result = true;
65 if (tag == m_tag && !m_acceptedValues.contains(str: value)) {
66 qCDebug(KNEWSTUFFCORE) << "Item excluded by filter on" << m_tag << "because" << value << "was not included in" << m_acceptedValues;
67 result = false;
68 }
69 return result;
70 }
71 };
72
73 // Will only accept entries which have none of the values set for the tag key
74 class InequalityValidator : public Validator
75 {
76 public:
77 InequalityValidator(const QString &tag, const QString &value)
78 : Validator(tag, value)
79 {
80 }
81 ~InequalityValidator() override
82 {
83 }
84 bool filterAccepts(const QString &tag, const QString &value) override
85 {
86 bool result = true;
87 if (tag == m_tag && m_acceptedValues.contains(str: value)) {
88 qCDebug(KNEWSTUFFCORE) << "Item excluded by filter on" << m_tag << "because" << value << "was included in" << m_acceptedValues;
89 result = false;
90 }
91 return result;
92 }
93 };
94
95 void addValidator(const QString &filter)
96 {
97 int pos = 0;
98 if ((pos = filter.indexOf(s: QLatin1String("=="))) > -1) {
99 QString tag = filter.left(n: pos);
100 QString value = filter.mid(position: tag.length() + 2);
101 Validator *val = validators.value(key: tag, defaultValue: nullptr);
102 if (!val) {
103 val = new EqualityValidator(tag, QString());
104 validators.insert(key: tag, value: val);
105 }
106 val->m_acceptedValues << value;
107 qCDebug(KNEWSTUFFCORE) << "Created EqualityValidator for tag" << tag << "with value" << value;
108 } else if ((pos = filter.indexOf(s: QLatin1String("!="))) > -1) {
109 QString tag = filter.left(n: pos);
110 QString value = filter.mid(position: tag.length() + 2);
111 Validator *val = validators.value(key: tag, defaultValue: nullptr);
112 if (!val) {
113 val = new InequalityValidator(tag, QString());
114 validators.insert(key: tag, value: val);
115 }
116 val->m_acceptedValues << value;
117 qCDebug(KNEWSTUFFCORE) << "Created InequalityValidator for tag" << tag << "with value" << value;
118 } else {
119 qCDebug(KNEWSTUFFCORE) << "Critical error attempting to create tag filter validators. The filter is defined as" << filter
120 << "which is not in the accepted formats key==value or key!=value";
121 }
122 }
123};
124
125TagsFilterChecker::TagsFilterChecker(const QStringList &tagFilter)
126 : d(new TagsFilterCheckerPrivate)
127{
128 for (const QString &filter : tagFilter) {
129 d->addValidator(filter);
130 }
131}
132
133TagsFilterChecker::~TagsFilterChecker() = default;
134
135bool TagsFilterChecker::filterAccepts(const QStringList &tags)
136{
137 // if any tag in the content matches any of the tag filters, skip this entry
138 qCDebug(KNEWSTUFFCORE) << "Checking tags list" << tags << "against validators with keys" << d->validators.keys();
139 for (const QString &tag : tags) {
140 if (tag.isEmpty()) {
141 // This happens when you do a split on an empty string (not an empty list, a list with one empty element... because reasons).
142 // Also handy for other things, i guess, though, so let's just catch it here.
143 continue;
144 }
145 QStringList current = tag.split(sep: QLatin1Char('='));
146 if (current.length() > 2) {
147 qCDebug(KNEWSTUFFCORE) << "Critical error attempting to filter tags. Entry has tag defined as" << tag
148 << "which is not in the format \"key=value\" or \"key\".";
149 return false;
150 } else if (current.length() == 1) {
151 // If the tag is defined simply as a key, we give it the value "1", just to make our filtering work simpler
152 current << QStringLiteral("1");
153 }
154 QMap<QString, TagsFilterCheckerPrivate::Validator *>::const_iterator i = d->validators.constBegin();
155 while (i != d->validators.constEnd()) {
156 if (!i.value()->filterAccepts(tag: current.at(i: 0), value: current.at(i: 1))) {
157 return false;
158 }
159 ++i;
160 }
161 }
162 // If we have arrived here, nothing has filtered the entry
163 // out (by being either incorrectly tagged or a filter rejecting
164 // it), and consequently it is an acceptable entry.
165 return true;
166}
167
168}
169

source code of knewstuff/src/core/tagsfilterchecker.cpp