File indexing completed on 2025-02-09 04:28:37
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 "filterexpression.h" 0011 0012 #include <QRegularExpression> 0013 0014 #include "exception.h" 0015 #include "filter.h" 0016 #include "parser.h" 0017 #include "util.h" 0018 0019 using ArgFilter = std::pair<QSharedPointer<KTextTemplate::Filter>, KTextTemplate::Variable>; 0020 0021 namespace KTextTemplate 0022 { 0023 0024 class FilterExpressionPrivate 0025 { 0026 FilterExpressionPrivate(FilterExpression *fe) 0027 : q_ptr(fe) 0028 { 0029 } 0030 0031 Variable m_variable; 0032 QList<ArgFilter> m_filters; 0033 QStringList m_filterNames; 0034 0035 Q_DECLARE_PUBLIC(FilterExpression) 0036 FilterExpression *const q_ptr; 0037 }; 0038 } 0039 0040 using namespace KTextTemplate; 0041 0042 static const char FILTER_SEPARATOR = '|'; 0043 static const char FILTER_ARGUMENT_SEPARATOR = ':'; 0044 0045 static QRegularExpression getFilterRegexp() 0046 { 0047 const QString filterSep(QRegularExpression::escape(QChar::fromLatin1(FILTER_SEPARATOR))); 0048 const QString argSep(QRegularExpression::escape(QChar::fromLatin1(FILTER_ARGUMENT_SEPARATOR))); 0049 0050 const QLatin1String varChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_."); 0051 const QLatin1String numChars(R"([-+\.]?\d[\d\.e]*)"); 0052 const QString i18nOpen(QRegularExpression::escape(QStringLiteral("_("))); 0053 const QLatin1String doubleQuoteStringLiteral(R"("[^"\\]*(?:\\.[^"\\]*)*")"); 0054 const QLatin1String singleQuoteStringLiteral(R"('[^'\\]*(?:\\.[^'\\]*)*')"); 0055 const QString i18nClose(QRegularExpression::escape(QStringLiteral(")"))); 0056 const QString variable = QLatin1Char('[') + varChars + QStringLiteral("]+"); 0057 0058 const QString localizedExpression = QStringLiteral("(?:") + i18nOpen + doubleQuoteStringLiteral + i18nClose + QLatin1Char('|') + i18nOpen 0059 + singleQuoteStringLiteral + i18nClose + QLatin1Char('|') + i18nOpen + numChars + i18nClose + QLatin1Char('|') + i18nOpen + variable + i18nClose 0060 + QLatin1Char(')'); 0061 0062 const QString constantString = QStringLiteral("(?:") + doubleQuoteStringLiteral + QLatin1Char('|') + singleQuoteStringLiteral + QLatin1Char(')'); 0063 0064 const QString filterRawString = QLatin1Char('^') + constantString + QLatin1Char('|') + QLatin1Char('^') + localizedExpression + QLatin1Char('|') 0065 + QLatin1Char('^') + variable + QLatin1Char('|') + numChars + QLatin1Char('|') + filterSep + QStringLiteral("\\w+|") + argSep + QStringLiteral("(?:") 0066 + constantString + QLatin1Char('|') + localizedExpression + QLatin1Char('|') + variable + QLatin1Char('|') + numChars + QLatin1Char('|') + filterSep 0067 + QStringLiteral("\\w+)"); 0068 0069 return QRegularExpression(filterRawString); 0070 } 0071 0072 FilterExpression::FilterExpression(const QString &varString, Parser *parser) 0073 : d_ptr(new FilterExpressionPrivate(this)) 0074 { 0075 Q_D(FilterExpression); 0076 0077 auto pos = 0; 0078 auto lastPos = 0; 0079 int len; 0080 QString subString; 0081 0082 static const auto sFilterRe = getFilterRegexp(); 0083 0084 // This is one fo the few constructors that can throw so we make sure to 0085 // delete its d->pointer. 0086 try { 0087 auto i = sFilterRe.globalMatch(varString); 0088 while (i.hasNext()) { 0089 auto match = i.next(); 0090 len = match.capturedLength(); 0091 pos = match.capturedStart(); 0092 subString = match.captured(); 0093 const auto ssSize = subString.size(); 0094 0095 if (pos != lastPos) { 0096 throw KTextTemplate::Exception(TagSyntaxError, QStringLiteral("Could not parse some characters: \"%1\"").arg(varString.mid(lastPos, pos))); 0097 } 0098 0099 if (subString.startsWith(QLatin1Char(FILTER_SEPARATOR))) { 0100 subString = subString.right(ssSize - 1); 0101 auto f = parser->getFilter(subString); 0102 0103 Q_ASSERT(f); 0104 0105 d->m_filterNames << subString; 0106 d->m_filters << std::make_pair(f, Variable()); 0107 0108 } else if (subString.startsWith(QLatin1Char(FILTER_ARGUMENT_SEPARATOR))) { 0109 if (d->m_filters.isEmpty() || d->m_filters.at(d->m_filters.size() - 1).second.isValid()) { 0110 const auto remainder = varString.right(varString.size() - lastPos); 0111 throw KTextTemplate::Exception(TagSyntaxError, QStringLiteral("Could not parse the remainder, %1 from %2").arg(remainder, varString)); 0112 } 0113 subString = subString.right(ssSize - 1); 0114 const auto lastFilter = d->m_filters.size(); 0115 if (subString.startsWith(QLatin1Char(FILTER_SEPARATOR))) 0116 throw KTextTemplate::Exception(EmptyVariableError, QStringLiteral("Missing argument to filter: %1").arg(d->m_filterNames[lastFilter - 1])); 0117 0118 d->m_filters[lastFilter - 1].second = Variable(subString); 0119 } else { 0120 // Token is _("translated"), or "constant", or a variable; 0121 d->m_variable = Variable(subString); 0122 } 0123 0124 pos += len; 0125 lastPos = pos; 0126 } 0127 0128 const auto remainder = varString.right(varString.size() - lastPos); 0129 if (!remainder.isEmpty()) { 0130 throw KTextTemplate::Exception(TagSyntaxError, QStringLiteral("Could not parse the remainder, %1 from %2").arg(remainder, varString)); 0131 } 0132 } catch (...) { 0133 delete d_ptr; 0134 throw; 0135 } 0136 } 0137 0138 FilterExpression::FilterExpression(const FilterExpression &other) 0139 : d_ptr(new FilterExpressionPrivate(this)) 0140 { 0141 *this = other; 0142 } 0143 0144 FilterExpression::FilterExpression() 0145 : d_ptr(new FilterExpressionPrivate(this)) 0146 { 0147 } 0148 0149 bool FilterExpression::isValid() const 0150 { 0151 Q_D(const FilterExpression); 0152 return d->m_variable.isValid(); 0153 } 0154 0155 FilterExpression::~FilterExpression() 0156 { 0157 delete d_ptr; 0158 } 0159 0160 Variable FilterExpression::variable() const 0161 { 0162 Q_D(const FilterExpression); 0163 return d->m_variable; 0164 } 0165 0166 FilterExpression &FilterExpression::operator=(const FilterExpression &other) 0167 { 0168 if (&other == this) 0169 return *this; 0170 d_ptr->m_variable = other.d_ptr->m_variable; 0171 d_ptr->m_filters = other.d_ptr->m_filters; 0172 d_ptr->m_filterNames = other.d_ptr->m_filterNames; 0173 return *this; 0174 } 0175 0176 QVariant FilterExpression::resolve(OutputStream *stream, Context *c) const 0177 { 0178 Q_D(const FilterExpression); 0179 auto var = d->m_variable.resolve(c); 0180 0181 auto it = d->m_filters.constBegin(); 0182 const auto end = d->m_filters.constEnd(); 0183 for (; it != end; ++it) { 0184 auto filter = it->first; 0185 filter->setStream(stream); 0186 const auto argVar = it->second; 0187 auto arg = argVar.resolve(c); 0188 0189 if (arg.isValid()) { 0190 KTextTemplate::SafeString argString; 0191 if (arg.userType() == qMetaTypeId<KTextTemplate::SafeString>()) { 0192 argString = arg.value<KTextTemplate::SafeString>(); 0193 } else if (arg.userType() == qMetaTypeId<QString>()) { 0194 argString = KTextTemplate::SafeString(arg.value<QString>()); 0195 } 0196 if (argVar.isConstant()) { 0197 argString = markSafe(argString); 0198 } 0199 if (!argString.get().isEmpty()) { 0200 arg = argString; 0201 } 0202 } 0203 0204 const auto varString = getSafeString(var); 0205 0206 var = filter->doFilter(var, arg, c->autoEscape()); 0207 0208 if (var.userType() == qMetaTypeId<KTextTemplate::SafeString>() || var.userType() == qMetaTypeId<QString>()) { 0209 if (filter->isSafe() && varString.isSafe()) { 0210 var = markSafe(getSafeString(var)); 0211 } else if (varString.needsEscape()) { 0212 var = markForEscaping(getSafeString(var)); 0213 } else { 0214 var = getSafeString(var); 0215 } 0216 } 0217 } 0218 (*stream) << getSafeString(var).get(); 0219 return var; 0220 } 0221 0222 QVariant FilterExpression::resolve(Context *c) const 0223 { 0224 OutputStream _dummy; 0225 return resolve(&_dummy, c); 0226 } 0227 0228 QVariantList FilterExpression::toList(Context *c) const 0229 { 0230 const auto var = resolve(c); 0231 if (!var.canConvert<QVariantList>()) 0232 return {}; 0233 return var.value<QVariantList>(); 0234 } 0235 0236 bool FilterExpression::isTrue(Context *c) const 0237 { 0238 return variantIsTrue(resolve(c)); 0239 } 0240 0241 QStringList FilterExpression::filters() const 0242 { 0243 Q_D(const FilterExpression); 0244 return d->m_filterNames; 0245 }