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

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 "node.h"
0011 
0012 #include "metaenumvariable_p.h"
0013 #include "nodebuiltins_p.h"
0014 #include "template.h"
0015 #include "util.h"
0016 
0017 #include <QRegularExpressionMatchIterator>
0018 
0019 using namespace KTextTemplate;
0020 
0021 namespace KTextTemplate
0022 {
0023 
0024 class NodePrivate
0025 {
0026     NodePrivate(Node *node)
0027         : q_ptr(node)
0028     {
0029     }
0030     Q_DECLARE_PUBLIC(Node)
0031     Node *const q_ptr;
0032 };
0033 
0034 class AbstractNodeFactoryPrivate
0035 {
0036     AbstractNodeFactoryPrivate(AbstractNodeFactory *factory)
0037         : q_ptr(factory)
0038     {
0039 #if defined(Q_CC_MSVC)
0040 // MSVC doesn't like static string concatenations like L"foo" "bar", as
0041 // results from QStringLiteral, so use QLatin1String here instead.
0042 #define STRING_LITERAL QLatin1String
0043 #else
0044 #define STRING_LITERAL QStringLiteral
0045 #endif
0046         smartSplitRe =
0047             QRegularExpression(STRING_LITERAL("(" // match
0048                                               "(?:[^\\s\\\'\\\"]*" // things that are not whitespace or
0049                                                                    // escaped quote chars
0050                                               "(?:" // followed by
0051                                               "(?:\"" // Either a quote starting with "
0052                                               "(?:[^\"\\\\]|\\\\.)*\"" // followed by anything that is
0053                                                                        // not the end of the quote
0054                                               "|\'" // Or a quote starting with '
0055                                               "(?:[^\'\\\\]|\\\\.)*\'" // followed by anything that is
0056                                                                        // not the end of the quote
0057                                               ")" // (End either)
0058                                               "[^\\s\'\"]*" // To the start of the next such fragment
0059                                               ")+" // Perform multiple matches of the above.
0060                                               ")" // End of quoted string handling.
0061                                               "|\\S+" // Apart from quoted strings, match
0062                                                       // non-whitespace fragments also
0063                                               ")" // End match
0064                                               ));
0065 
0066 #undef STRING_LITERAL
0067     }
0068 
0069     Q_DECLARE_PUBLIC(AbstractNodeFactory)
0070     AbstractNodeFactory *const q_ptr;
0071 
0072 public:
0073     QRegularExpression smartSplitRe;
0074 };
0075 }
0076 
0077 Node::Node(QObject *parent)
0078     : QObject(parent)
0079     , d_ptr(new NodePrivate(this))
0080 {
0081 }
0082 
0083 Node::~Node()
0084 {
0085     delete d_ptr;
0086 }
0087 
0088 void Node::streamValueInContext(OutputStream *stream, const QVariant &input, Context *c) const
0089 {
0090     KTextTemplate::SafeString inputString;
0091     if (input.userType() == qMetaTypeId<QVariantList>()) {
0092         inputString = toString(input.value<QVariantList>());
0093     } else if (input.userType() == qMetaTypeId<MetaEnumVariable>()) {
0094         const auto mev = input.value<MetaEnumVariable>();
0095         if (mev.value >= 0)
0096             (*stream) << QString::number(mev.value);
0097     } else {
0098         inputString = getSafeString(input);
0099     }
0100     if (c->autoEscape() && !inputString.isSafe())
0101         inputString.setNeedsEscape(true);
0102 
0103     (*stream) << inputString;
0104 }
0105 
0106 TemplateImpl *Node::containerTemplate() const
0107 {
0108     auto _parent = parent();
0109     auto ti = qobject_cast<TemplateImpl *>(_parent);
0110     while (_parent && !ti) {
0111         _parent = _parent->parent();
0112         ti = qobject_cast<TemplateImpl *>(_parent);
0113     }
0114     Q_ASSERT(ti);
0115     return ti;
0116 }
0117 
0118 NodeList::NodeList()
0119     : m_containsNonText(false)
0120 {
0121 }
0122 
0123 NodeList::NodeList(const NodeList &list) = default;
0124 
0125 NodeList &NodeList::operator=(const NodeList &list)
0126 {
0127     if (&list == this) {
0128         return *this;
0129     }
0130     static_cast<QList<KTextTemplate::Node *> &>(*this) = static_cast<QList<KTextTemplate::Node *>>(list);
0131     m_containsNonText = list.m_containsNonText;
0132     return *this;
0133 }
0134 
0135 NodeList::NodeList(const QList<KTextTemplate::Node *> &list)
0136     : QList<KTextTemplate::Node *>(list)
0137     , m_containsNonText(false)
0138 {
0139     for (KTextTemplate::Node *node : list) {
0140         auto textNode = qobject_cast<TextNode *>(node);
0141         if (!textNode) {
0142             m_containsNonText = true;
0143             return;
0144         }
0145     }
0146 }
0147 
0148 NodeList::~NodeList() = default;
0149 
0150 void NodeList::append(KTextTemplate::Node *node)
0151 {
0152     if (!m_containsNonText) {
0153         auto textNode = qobject_cast<TextNode *>(node);
0154         if (!textNode)
0155             m_containsNonText = true;
0156     }
0157 
0158     QList<KTextTemplate::Node *>::append(node);
0159 }
0160 
0161 void NodeList::append(const QList<KTextTemplate::Node *> &nodeList)
0162 {
0163     if (!m_containsNonText) {
0164         for (KTextTemplate::Node *node : nodeList) {
0165             auto textNode = qobject_cast<TextNode *>(node);
0166             if (!textNode) {
0167                 m_containsNonText = true;
0168                 break;
0169             }
0170         }
0171     }
0172 
0173     QList<KTextTemplate::Node *>::append(nodeList);
0174 }
0175 
0176 bool NodeList::containsNonText() const
0177 {
0178     return m_containsNonText;
0179 }
0180 
0181 void NodeList::render(OutputStream *stream, Context *c) const
0182 {
0183     for (auto i = 0; i < this->size(); ++i) {
0184         this->at(i)->render(stream, c);
0185     }
0186 }
0187 
0188 AbstractNodeFactory::AbstractNodeFactory(QObject *parent)
0189     : QObject(parent)
0190     , d_ptr(new AbstractNodeFactoryPrivate(this))
0191 {
0192 }
0193 
0194 AbstractNodeFactory::~AbstractNodeFactory()
0195 {
0196     delete d_ptr;
0197 }
0198 
0199 QList<FilterExpression> AbstractNodeFactory::getFilterExpressionList(const QStringList &list, Parser *p) const
0200 {
0201     QList<FilterExpression> fes;
0202     for (auto &varString : list) {
0203         fes << FilterExpression(varString, p);
0204     }
0205     return fes;
0206 }
0207 
0208 QStringList AbstractNodeFactory::smartSplit(const QString &str) const
0209 {
0210     Q_D(const AbstractNodeFactory);
0211     QStringList l;
0212 
0213     auto i = d->smartSplitRe.globalMatch(str);
0214     while (i.hasNext()) {
0215         auto match = i.next();
0216         l.append(match.captured());
0217     }
0218 
0219     return l;
0220 }
0221 
0222 #include "moc_node.cpp"