File indexing completed on 2024-05-12 04:01:51

0001 /*
0002     SPDX-FileCopyrightText: 2006 Kevin Ottens <ervin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "predicate.h"
0008 
0009 #include <QMetaEnum>
0010 #include <QSequentialIterable>
0011 #include <QStringList>
0012 #include <solid/device.h>
0013 
0014 namespace Solid
0015 {
0016 class Predicate::Private
0017 {
0018 public:
0019     Private()
0020         : isValid(false)
0021         , type(PropertyCheck)
0022         , compOperator(Predicate::Equals)
0023         , operand1(nullptr)
0024         , operand2(nullptr)
0025     {
0026     }
0027 
0028     bool isValid;
0029     Type type;
0030 
0031     DeviceInterface::Type ifaceType;
0032     QString property;
0033     QVariant value;
0034     Predicate::ComparisonOperator compOperator;
0035 
0036     Predicate *operand1;
0037     Predicate *operand2;
0038 };
0039 }
0040 
0041 Solid::Predicate::Predicate()
0042     : d(new Private())
0043 {
0044 }
0045 
0046 Solid::Predicate::Predicate(const Predicate &other)
0047     : d(new Private())
0048 {
0049     *this = other;
0050 }
0051 
0052 Solid::Predicate::Predicate(const DeviceInterface::Type &ifaceType, const QString &property, const QVariant &value, ComparisonOperator compOperator)
0053     : d(new Private())
0054 {
0055     d->isValid = true;
0056     d->ifaceType = ifaceType;
0057     d->property = property;
0058     d->value = value;
0059     d->compOperator = compOperator;
0060 }
0061 
0062 Solid::Predicate::Predicate(const QString &ifaceName, const QString &property, const QVariant &value, ComparisonOperator compOperator)
0063     : d(new Private())
0064 {
0065     DeviceInterface::Type ifaceType = DeviceInterface::stringToType(ifaceName);
0066 
0067     if (((int)ifaceType) != -1) {
0068         d->isValid = true;
0069         d->ifaceType = ifaceType;
0070         d->property = property;
0071         d->value = value;
0072         d->compOperator = compOperator;
0073     }
0074 }
0075 
0076 Solid::Predicate::Predicate(const DeviceInterface::Type &ifaceType)
0077     : d(new Private())
0078 {
0079     d->isValid = true;
0080     d->type = InterfaceCheck;
0081     d->ifaceType = ifaceType;
0082 }
0083 
0084 Solid::Predicate::Predicate(const QString &ifaceName)
0085     : d(new Private())
0086 {
0087     DeviceInterface::Type ifaceType = DeviceInterface::stringToType(ifaceName);
0088 
0089     if (((int)ifaceType) != -1) {
0090         d->isValid = true;
0091         d->type = InterfaceCheck;
0092         d->ifaceType = ifaceType;
0093     }
0094 }
0095 
0096 Solid::Predicate::~Predicate()
0097 {
0098     if (d->type != PropertyCheck && d->type != InterfaceCheck) {
0099         delete d->operand1;
0100         delete d->operand2;
0101     }
0102 
0103     delete d;
0104 }
0105 
0106 Solid::Predicate &Solid::Predicate::operator=(const Predicate &other)
0107 {
0108     d->isValid = other.d->isValid;
0109     d->type = other.d->type;
0110 
0111     if (d->type != PropertyCheck && d->type != InterfaceCheck) {
0112         Predicate *operand1 = new Predicate(*(other.d->operand1));
0113         delete d->operand1;
0114         d->operand1 = operand1;
0115         Predicate *operand2 = new Predicate(*(other.d->operand2));
0116         delete d->operand2;
0117         d->operand2 = operand2;
0118     } else {
0119         d->ifaceType = other.d->ifaceType;
0120         d->property = other.d->property;
0121         d->value = other.d->value;
0122         d->compOperator = other.d->compOperator;
0123     }
0124 
0125     return *this;
0126 }
0127 
0128 Solid::Predicate Solid::Predicate::operator&(const Predicate &other)
0129 {
0130     Predicate result;
0131 
0132     result.d->isValid = true;
0133     result.d->type = Conjunction;
0134     result.d->operand1 = new Predicate(*this);
0135     result.d->operand2 = new Predicate(other);
0136 
0137     return result;
0138 }
0139 
0140 Solid::Predicate &Solid::Predicate::operator&=(const Predicate &other)
0141 {
0142     *this = *this & other;
0143     return *this;
0144 }
0145 
0146 Solid::Predicate Solid::Predicate::operator|(const Predicate &other)
0147 {
0148     Predicate result;
0149 
0150     result.d->isValid = true;
0151     result.d->type = Disjunction;
0152     result.d->operand1 = new Predicate(*this);
0153     result.d->operand2 = new Predicate(other);
0154 
0155     return result;
0156 }
0157 
0158 Solid::Predicate &Solid::Predicate::operator|=(const Predicate &other)
0159 {
0160     *this = *this | other;
0161     return *this;
0162 }
0163 
0164 bool Solid::Predicate::isValid() const
0165 {
0166     return d->isValid;
0167 }
0168 
0169 bool Solid::Predicate::matches(const Device &device) const
0170 {
0171     if (!d->isValid) {
0172         return false;
0173     }
0174 
0175     switch (d->type) {
0176     case Disjunction:
0177         return d->operand1->matches(device) || d->operand2->matches(device);
0178     case Conjunction:
0179         return d->operand1->matches(device) && d->operand2->matches(device);
0180     case PropertyCheck: {
0181         const DeviceInterface *iface = device.asDeviceInterface(d->ifaceType);
0182 
0183         if (iface != nullptr) {
0184             const int index = iface->metaObject()->indexOfProperty(d->property.toLatin1());
0185             QMetaProperty metaProp = iface->metaObject()->property(index);
0186             QVariant value = metaProp.isReadable() ? metaProp.read(iface) : QVariant();
0187             QVariant expected = d->value;
0188 
0189             if (metaProp.isEnumType() && expected.userType() == QMetaType::QString) {
0190                 QMetaEnum metaEnum = metaProp.enumerator();
0191                 int value = metaEnum.keysToValue(d->value.toString().toLatin1());
0192                 if (value >= 0) { // No value found for these keys, resetting expected to invalid
0193                     expected = QVariant(metaProp.metaType(), &value);
0194                 } else {
0195                     expected = QVariant();
0196                 }
0197             } else if (metaProp.isEnumType() && expected.userType() == QMetaType::Int) {
0198                 int expectedValue = expected.toInt();
0199                 expected = QVariant(metaProp.metaType(), &expectedValue);
0200             }
0201 
0202             if (d->compOperator == Mask) {
0203                 bool v_ok;
0204                 int v = value.toInt(&v_ok);
0205                 bool e_ok;
0206                 int e = expected.toInt(&e_ok);
0207 
0208                 return (e_ok && v_ok && (v & e));
0209             }
0210 
0211             if (value == expected) {
0212                 return true;
0213             }
0214 
0215             // Make sure we can match single elements inside lists.
0216             if (value.canConvert<QSequentialIterable>()) {
0217                 const auto iterable = value.value<QSequentialIterable>();
0218                 for (const auto &element : iterable) {
0219                     if (element == expected) {
0220                         return true;
0221                     }
0222                 }
0223             }
0224         }
0225         break;
0226     }
0227     case InterfaceCheck:
0228         return device.isDeviceInterface(d->ifaceType);
0229     }
0230 
0231     return false;
0232 }
0233 
0234 QSet<Solid::DeviceInterface::Type> Solid::Predicate::usedTypes() const
0235 {
0236     QSet<DeviceInterface::Type> res;
0237 
0238     if (d->isValid) {
0239         switch (d->type) {
0240         case Disjunction:
0241         case Conjunction:
0242             res += d->operand1->usedTypes();
0243             res += d->operand2->usedTypes();
0244             break;
0245         case PropertyCheck:
0246         case InterfaceCheck:
0247             res << d->ifaceType;
0248             break;
0249         }
0250     }
0251 
0252     return res;
0253 }
0254 
0255 QString Solid::Predicate::toString() const
0256 {
0257     if (!d->isValid) {
0258         return "False";
0259     }
0260 
0261     if (d->type != PropertyCheck && d->type != InterfaceCheck) {
0262         QString op = " AND ";
0263         if (d->type == Disjunction) {
0264             op = " OR ";
0265         }
0266 
0267         return '[' + d->operand1->toString() + op + d->operand2->toString() + ']';
0268     } else {
0269         QString ifaceName = DeviceInterface::typeToString(d->ifaceType);
0270 
0271         if (ifaceName.isEmpty()) {
0272             ifaceName = "Unknown";
0273         }
0274 
0275         if (d->type == InterfaceCheck) {
0276             return "IS " + ifaceName;
0277         }
0278 
0279         QString value;
0280 
0281         switch (d->value.userType()) {
0282         case QMetaType::QStringList: {
0283             value = '{';
0284 
0285             const QStringList list = d->value.toStringList();
0286 
0287             QStringList::ConstIterator it = list.begin();
0288             QStringList::ConstIterator end = list.end();
0289 
0290             for (; it != end; ++it) {
0291                 value += '\'' + *it + '\'';
0292 
0293                 if (it + 1 != end) {
0294                     value += ", ";
0295                 }
0296             }
0297 
0298             value += '}';
0299             break;
0300         }
0301         case QMetaType::Bool:
0302             value = (d->value.toBool() ? "true" : "false");
0303             break;
0304         case QMetaType::Int:
0305         case QMetaType::UInt:
0306         case QMetaType::LongLong:
0307         case QMetaType::ULongLong:
0308             value = d->value.toString();
0309             break;
0310         default:
0311             value = '\'' + d->value.toString() + '\'';
0312             break;
0313         }
0314 
0315         QString str_operator = "==";
0316         if (d->compOperator != Equals) {
0317             str_operator = " &";
0318         }
0319 
0320         return ifaceName + '.' + d->property + ' ' + str_operator + ' ' + value;
0321     }
0322 }
0323 
0324 Solid::Predicate::Type Solid::Predicate::type() const
0325 {
0326     return d->type;
0327 }
0328 
0329 Solid::DeviceInterface::Type Solid::Predicate::interfaceType() const
0330 {
0331     return d->ifaceType;
0332 }
0333 
0334 QString Solid::Predicate::propertyName() const
0335 {
0336     return d->property;
0337 }
0338 
0339 QVariant Solid::Predicate::matchingValue() const
0340 {
0341     return d->value;
0342 }
0343 
0344 Solid::Predicate::ComparisonOperator Solid::Predicate::comparisonOperator() const
0345 {
0346     return d->compOperator;
0347 }
0348 
0349 Solid::Predicate Solid::Predicate::firstOperand() const
0350 {
0351     if (d->operand1) {
0352         return *d->operand1;
0353     }
0354     return Predicate();
0355 }
0356 
0357 Solid::Predicate Solid::Predicate::secondOperand() const
0358 {
0359     if (d->operand2) {
0360         return *d->operand2;
0361     }
0362     return Predicate();
0363 }