File indexing completed on 2025-02-09 04:28:41

0001 /*
0002   This file is part of the KTextTemplate library
0003 
0004   SPDX-FileCopyrightText: 2009, 2010 Stephen Kelly <steveire@gmail.com>
0005 
0006   SPDX-License-Identifier: LGPL-2.1-or-later
0007 
0008 */
0009 
0010 #include "variable.h"
0011 
0012 #include "abstractlocalizer.h"
0013 #include "context.h"
0014 #include "exception.h"
0015 #include "metaenumvariable_p.h"
0016 #include "metatype.h"
0017 #include "util.h"
0018 
0019 #include <QMetaEnum>
0020 #include <QStringList>
0021 
0022 using namespace KTextTemplate;
0023 
0024 namespace KTextTemplate
0025 {
0026 
0027 class VariablePrivate
0028 {
0029 public:
0030     VariablePrivate(Variable *variable)
0031         : q_ptr(variable)
0032     {
0033     }
0034 
0035     Q_DECLARE_PUBLIC(Variable)
0036     Variable *const q_ptr;
0037 
0038     QString m_varString;
0039     QVariant m_literal;
0040     QStringList m_lookups;
0041     bool m_localize = false;
0042 };
0043 }
0044 
0045 Variable::Variable(const Variable &other)
0046     : d_ptr(new VariablePrivate(this))
0047 {
0048     *this = other;
0049 }
0050 
0051 Variable::Variable()
0052     : d_ptr(new VariablePrivate(this))
0053 {
0054 }
0055 
0056 Variable::~Variable()
0057 {
0058     delete d_ptr;
0059 }
0060 
0061 Variable &Variable::operator=(const Variable &other)
0062 {
0063     if (&other == this)
0064         return *this;
0065     d_ptr->m_varString = other.d_ptr->m_varString;
0066     d_ptr->m_literal = other.d_ptr->m_literal;
0067     d_ptr->m_lookups = other.d_ptr->m_lookups;
0068     d_ptr->m_localize = other.d_ptr->m_localize;
0069     return *this;
0070 }
0071 
0072 Variable::Variable(const QString &var)
0073     : d_ptr(new VariablePrivate(this))
0074 {
0075     Q_D(Variable);
0076     d->m_varString = var;
0077 
0078     auto localVar = var;
0079     if (var.startsWith(QStringLiteral("_("))) {
0080         // The FilterExpression parser ensures this:
0081         Q_ASSERT(var.endsWith(QLatin1Char(')')));
0082         d->m_localize = true;
0083         localVar = var.mid(2, var.size() - 3);
0084     }
0085     if (localVar.endsWith(QLatin1Char('.'))) {
0086         delete d_ptr;
0087         throw KTextTemplate::Exception(TagSyntaxError, QStringLiteral("Variable may not end with a dot: %1").arg(localVar));
0088     }
0089 
0090     auto processedNumber = false;
0091     {
0092         const auto intResult = QLocale::c().toInt(localVar, &processedNumber);
0093         if (processedNumber) {
0094             d->m_literal = intResult;
0095         } else {
0096             const auto doubleResult = QLocale::c().toDouble(localVar, &processedNumber);
0097             if (processedNumber) {
0098                 d->m_literal = doubleResult;
0099             }
0100         }
0101     }
0102     if (!processedNumber) {
0103         if (localVar.startsWith(QLatin1Char('"')) || localVar.startsWith(QLatin1Char('\''))) {
0104             // The FilterExpression parser ensures this:
0105             Q_ASSERT(localVar.endsWith(QLatin1Char('\'')) || localVar.endsWith(QLatin1Char('"')));
0106             const auto unesc = unescapeStringLiteral(localVar);
0107             const auto ss = markSafe(unesc);
0108             d->m_literal = QVariant::fromValue<KTextTemplate::SafeString>(ss);
0109         } else {
0110             if (localVar.contains(QStringLiteral("._")) || (localVar.startsWith(QLatin1Char('_')))) {
0111                 delete d_ptr;
0112                 throw KTextTemplate::Exception(TagSyntaxError, QStringLiteral("Variables and attributes may not begin with underscores: %1").arg(localVar));
0113             }
0114             d->m_lookups = localVar.split(QLatin1Char('.'));
0115         }
0116     }
0117 }
0118 
0119 bool Variable::isValid() const
0120 {
0121     Q_D(const Variable);
0122     return !d->m_varString.isEmpty();
0123 }
0124 
0125 bool Variable::isConstant() const
0126 {
0127     Q_D(const Variable);
0128     return !d->m_literal.isNull();
0129 }
0130 
0131 bool Variable::isTrue(Context *c) const
0132 {
0133     return variantIsTrue(resolve(c));
0134 }
0135 
0136 bool Variable::isLocalized() const
0137 {
0138     Q_D(const Variable);
0139     return d->m_localize;
0140 }
0141 
0142 QVariant Variable::literal() const
0143 {
0144     Q_D(const Variable);
0145     return d->m_literal;
0146 }
0147 
0148 QStringList Variable::lookups() const
0149 {
0150     Q_D(const Variable);
0151     return d->m_lookups;
0152 }
0153 
0154 class StaticQtMetaObject : public QObject
0155 {
0156 public:
0157     static const QMetaObject *_smo()
0158     {
0159         return &Qt::staticMetaObject;
0160     }
0161 };
0162 
0163 QVariant Variable::resolve(Context *c) const
0164 {
0165     Q_D(const Variable);
0166     QVariant var;
0167     if (!d->m_lookups.isEmpty()) {
0168         auto i = 0;
0169         if (d->m_lookups.at(i) == QStringLiteral("Qt")) {
0170             ++i;
0171             if (d->m_lookups.size() <= i)
0172                 return {};
0173 
0174             const auto nextPart = d->m_lookups.at(i);
0175             ++i;
0176 
0177             static auto globalMetaObject = StaticQtMetaObject::_smo();
0178 
0179             auto breakout = false;
0180             for (auto j = 0; j < globalMetaObject->enumeratorCount(); ++j) {
0181                 const auto me = globalMetaObject->enumerator(j);
0182 
0183                 if (QLatin1String(me.name()) == nextPart) {
0184                     const MetaEnumVariable mev(me);
0185                     var = QVariant::fromValue(mev);
0186                     break;
0187                 }
0188 
0189                 for (auto k = 0; k < me.keyCount(); ++k) {
0190                     if (QLatin1String(me.key(k)) == nextPart) {
0191                         const MetaEnumVariable mev(me, k);
0192                         var = QVariant::fromValue(mev);
0193                         breakout = true;
0194                         break;
0195                     }
0196                 }
0197                 if (breakout)
0198                     break;
0199             }
0200             if (!var.isValid())
0201                 return {};
0202 
0203         } else {
0204             var = c->lookup(d->m_lookups.at(i++));
0205         }
0206         while (i < d->m_lookups.size()) {
0207             var = MetaType::lookup(var, d->m_lookups.at(i++));
0208             if (!var.isValid())
0209                 return {};
0210         }
0211     } else {
0212         if (isSafeString(d->m_literal))
0213             var = QVariant::fromValue(getSafeString(d->m_literal));
0214         else
0215             var = d->m_literal;
0216     }
0217 
0218     if (d->m_localize) {
0219         return c->localizer()->localize(var);
0220     }
0221     return var;
0222 }