1/*
2 SPDX-FileCopyrightText: 2006 Kevin Ottens <ervin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "predicate.h"
8
9#include <QMetaEnum>
10#include <QSequentialIterable>
11#include <QStringList>
12#include <solid/device.h>
13
14namespace Solid
15{
16class Predicate::Private
17{
18public:
19 Private()
20 : isValid(false)
21 , type(PropertyCheck)
22 , compOperator(Predicate::Equals)
23 , equalIFace(true)
24 , operand1(nullptr)
25 , operand2(nullptr)
26 {
27 }
28
29 bool isValid;
30 Type type;
31
32 DeviceInterface::Type ifaceType;
33 QString property;
34 QVariant value;
35 Predicate::ComparisonOperator compOperator;
36 bool equalIFace;
37
38 Predicate *operand1;
39 Predicate *operand2;
40};
41}
42
43Solid::Predicate::Predicate()
44 : d(new Private())
45{
46}
47
48Solid::Predicate::Predicate(const Predicate &other)
49 : d(new Private())
50{
51 *this = other;
52}
53
54Solid::Predicate::Predicate(const DeviceInterface::Type &ifaceType, const QString &property, const QVariant &value, ComparisonOperator compOperator)
55 : d(new Private())
56{
57 d->isValid = true;
58 d->ifaceType = ifaceType;
59 d->property = property;
60 d->value = value;
61 d->compOperator = compOperator;
62}
63
64Solid::Predicate::Predicate(const QString &ifaceName, const QString &property, const QVariant &value, ComparisonOperator compOperator)
65 : d(new Private())
66{
67 DeviceInterface::Type ifaceType = DeviceInterface::stringToType(type: ifaceName);
68
69 if (((int)ifaceType) != -1) {
70 d->isValid = true;
71 d->ifaceType = ifaceType;
72 d->property = property;
73 d->value = value;
74 d->compOperator = compOperator;
75 }
76}
77
78Solid::Predicate::Predicate(const DeviceInterface::Type &ifaceType)
79 : d(new Private())
80{
81 d->isValid = true;
82 d->type = InterfaceCheck;
83 d->ifaceType = ifaceType;
84}
85
86Solid::Predicate::Predicate(const QString &ifaceName)
87 : d(new Private())
88{
89 DeviceInterface::Type ifaceType = DeviceInterface::stringToType(type: ifaceName);
90
91 if (((int)ifaceType) != -1) {
92 d->isValid = true;
93 d->type = InterfaceCheck;
94 d->ifaceType = ifaceType;
95 }
96}
97
98Solid::Predicate::Predicate(QStringView ifaceName, NotOperator op)
99 : Predicate(ifaceName.toString())
100{
101 Q_UNUSED(op) // It's just there to make the API nicer
102 d->equalIFace = false;
103}
104
105Solid::Predicate::~Predicate()
106{
107 if (d->type != PropertyCheck && d->type != InterfaceCheck) {
108 delete d->operand1;
109 delete d->operand2;
110 }
111
112 delete d;
113}
114
115Solid::Predicate &Solid::Predicate::operator=(const Predicate &other)
116{
117 d->isValid = other.d->isValid;
118 d->type = other.d->type;
119 d->equalIFace = other.d->equalIFace;
120
121 if (d->type != PropertyCheck && d->type != InterfaceCheck) {
122 Predicate *operand1 = new Predicate(*(other.d->operand1));
123 delete d->operand1;
124 d->operand1 = operand1;
125 Predicate *operand2 = new Predicate(*(other.d->operand2));
126 delete d->operand2;
127 d->operand2 = operand2;
128 } else {
129 d->ifaceType = other.d->ifaceType;
130 d->property = other.d->property;
131 d->value = other.d->value;
132 d->compOperator = other.d->compOperator;
133 }
134
135 return *this;
136}
137
138Solid::Predicate Solid::Predicate::operator&(const Predicate &other)
139{
140 Predicate result;
141
142 result.d->isValid = true;
143 result.d->type = Conjunction;
144 result.d->operand1 = new Predicate(*this);
145 result.d->operand2 = new Predicate(other);
146
147 return result;
148}
149
150Solid::Predicate &Solid::Predicate::operator&=(const Predicate &other)
151{
152 *this = *this & other;
153 return *this;
154}
155
156Solid::Predicate Solid::Predicate::operator|(const Predicate &other)
157{
158 Predicate result;
159
160 result.d->isValid = true;
161 result.d->type = Disjunction;
162 result.d->operand1 = new Predicate(*this);
163 result.d->operand2 = new Predicate(other);
164
165 return result;
166}
167
168Solid::Predicate &Solid::Predicate::operator|=(const Predicate &other)
169{
170 *this = *this | other;
171 return *this;
172}
173
174bool Solid::Predicate::isValid() const
175{
176 return d->isValid;
177}
178
179bool Solid::Predicate::matches(const Device &device) const
180{
181 if (!d->isValid) {
182 return false;
183 }
184
185 switch (d->type) {
186 case Disjunction:
187 return d->operand1->matches(device) || d->operand2->matches(device);
188 case Conjunction:
189 return d->operand1->matches(device) && d->operand2->matches(device);
190 case PropertyCheck: {
191 const DeviceInterface *iface = device.asDeviceInterface(type: d->ifaceType);
192
193 if (iface != nullptr) {
194 const int index = iface->metaObject()->indexOfProperty(name: d->property.toLatin1().constData());
195 QMetaProperty metaProp = iface->metaObject()->property(index);
196 QVariant value = metaProp.isReadable() ? metaProp.read(obj: iface) : QVariant();
197 QVariant expected = d->value;
198
199 if (metaProp.isEnumType() && expected.userType() == QMetaType::QString) {
200 QMetaEnum metaEnum = metaProp.enumerator();
201 int value = metaEnum.keysToValue(keys: d->value.toString().toLatin1().constData());
202 if (value >= 0) { // No value found for these keys, resetting expected to invalid
203 expected = QVariant(metaProp.metaType(), &value);
204 } else {
205 expected = QVariant();
206 }
207 } else if (metaProp.isEnumType() && expected.userType() == QMetaType::Int) {
208 int expectedValue = expected.toInt();
209 expected = QVariant(metaProp.metaType(), &expectedValue);
210 }
211
212 if (d->compOperator == Mask) {
213 bool v_ok;
214 int v = value.toInt(ok: &v_ok);
215 bool e_ok;
216 int e = expected.toInt(ok: &e_ok);
217
218 return (e_ok && v_ok && (v & e));
219 }
220
221 if (value == expected) {
222 return true;
223 }
224
225 // Make sure we can match single elements inside lists.
226 if (value.canConvert<QSequentialIterable>()) {
227 const auto iterable = value.value<QSequentialIterable>();
228 for (const auto &element : iterable) {
229 if (element == expected) {
230 return true;
231 }
232 }
233 }
234 }
235 break;
236 }
237 case InterfaceCheck:
238 return device.isDeviceInterface(type: d->ifaceType) == d->equalIFace;
239 }
240
241 return false;
242}
243
244QSet<Solid::DeviceInterface::Type> Solid::Predicate::usedTypes() const
245{
246 QSet<DeviceInterface::Type> res;
247
248 if (d->isValid) {
249 switch (d->type) {
250 case Disjunction:
251 case Conjunction:
252 res += d->operand1->usedTypes();
253 res += d->operand2->usedTypes();
254 break;
255 case PropertyCheck:
256 case InterfaceCheck:
257 res << d->ifaceType;
258 break;
259 }
260 }
261
262 return res;
263}
264
265QString Solid::Predicate::toString() const
266{
267 if (!d->isValid) {
268 return QStringLiteral("False");
269 }
270
271 if (d->type != PropertyCheck && d->type != InterfaceCheck) {
272 QString op = QStringLiteral("AND");
273 if (d->type == Disjunction) {
274 op = QStringLiteral("OR");
275 }
276
277 return QStringLiteral("[%1 %2 %3]").arg(args: d->operand1->toString(), args&: op, args: d->operand2->toString());
278 } else {
279 QString ifaceName = DeviceInterface::typeToString(type: d->ifaceType);
280
281 if (ifaceName.isEmpty()) {
282 ifaceName = QStringLiteral("Unknown");
283 }
284
285 if (d->type == InterfaceCheck) {
286 return QStringLiteral("IS ") + ifaceName;
287 }
288
289 QString value;
290
291 switch (d->value.userType()) {
292 case QMetaType::QStringList: {
293 const QStringList list = d->value.toStringList();
294 if (list.isEmpty()) {
295 value = QStringLiteral("{}");
296 } else {
297 value = QStringLiteral("{'") + list.join(QStringLiteral("', '")) + QStringLiteral("'}'");
298 }
299 break;
300 }
301 case QMetaType::Bool:
302 value = (d->value.toBool() ? QStringLiteral("true") : QStringLiteral("false"));
303 break;
304 case QMetaType::Int:
305 case QMetaType::UInt:
306 case QMetaType::LongLong:
307 case QMetaType::ULongLong:
308 value = d->value.toString();
309 break;
310 default:
311 value = QStringLiteral("'%1'").arg(a: d->value.toString());
312 break;
313 }
314
315 QString str_operator = QStringLiteral("==");
316 if (d->compOperator != Equals) {
317 str_operator = QStringLiteral(" &");
318 }
319
320 return QStringLiteral("%1.%2 %3 %4").arg(args&: ifaceName, args&: d->property, args&: str_operator, args&: value);
321 }
322}
323
324Solid::Predicate::Type Solid::Predicate::type() const
325{
326 return d->type;
327}
328
329Solid::DeviceInterface::Type Solid::Predicate::interfaceType() const
330{
331 return d->ifaceType;
332}
333
334QString Solid::Predicate::propertyName() const
335{
336 return d->property;
337}
338
339QVariant Solid::Predicate::matchingValue() const
340{
341 return d->value;
342}
343
344Solid::Predicate::ComparisonOperator Solid::Predicate::comparisonOperator() const
345{
346 return d->compOperator;
347}
348
349Solid::Predicate Solid::Predicate::firstOperand() const
350{
351 if (d->operand1) {
352 return *d->operand1;
353 }
354 return Predicate();
355}
356
357Solid::Predicate Solid::Predicate::secondOperand() const
358{
359 if (d->operand2) {
360 return *d->operand2;
361 }
362 return Predicate();
363}
364
365bool Solid::Predicate::isInterfaceTypeNegated() const
366{
367 return !d->equalIFace;
368}
369

source code of solid/src/solid/devices/frontend/predicate.cpp