1 /*
2     SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "extractordocumentnode.h"
8 #include "extractordocumentprocessor.h"
9 #include "extractorfilter.h"
10 #include "extractorresult.h"
11 
12 #include <QJSEngine>
13 #include <QJSValue>
14 
15 #include <cassert>
16 
17 using namespace KItinerary;
18 
19 namespace KItinerary {
20 class ExtractorDocumentNodePrivate
21 {
22 public:
23     std::weak_ptr<ExtractorDocumentNodePrivate> parent;
24     std::vector<ExtractorDocumentNode> childNodes;
25     QString mimeType;
26     QVariant content;
27     QDateTime contextDateTime;
28     const ExtractorDocumentProcessor *processor;
29     ExtractorResult result;
30     QVariant location;
31     QJSEngine *m_jsEngine = nullptr;
32 
33     QJSEngine *jsEngine() const;
34 };
35 }
36 
jsEngine() const37 QJSEngine* ExtractorDocumentNodePrivate::jsEngine() const
38 {
39     if (m_jsEngine) {
40         return m_jsEngine;
41     }
42     const auto p = parent.lock();
43     return p ? p->jsEngine() : nullptr;
44 }
45 
ExtractorDocumentNode()46 ExtractorDocumentNode::ExtractorDocumentNode()
47     : d(std::make_shared<ExtractorDocumentNodePrivate>())
48 {
49 }
50 
ExtractorDocumentNode(const std::shared_ptr<ExtractorDocumentNodePrivate> & dd)51 ExtractorDocumentNode::ExtractorDocumentNode(const std::shared_ptr<ExtractorDocumentNodePrivate> &dd)
52     : d(dd ? dd : std::make_shared<ExtractorDocumentNodePrivate>())
53 {
54 }
55 
56 ExtractorDocumentNode::ExtractorDocumentNode(const ExtractorDocumentNode &other) = default;
57 ExtractorDocumentNode::ExtractorDocumentNode(ExtractorDocumentNode &&other) = default;
58 
~ExtractorDocumentNode()59 ExtractorDocumentNode::~ExtractorDocumentNode()
60 {
61     if (d && d.use_count() == 1 && d->processor) {
62         d->processor->destroyNode(*this);
63     }
64 }
65 
operator =(const ExtractorDocumentNode & other)66 ExtractorDocumentNode& ExtractorDocumentNode::operator=(const ExtractorDocumentNode &other)
67 {
68     if (d && d.use_count() == 1 && d->processor) {
69         d->processor->destroyNode(*this);
70     }
71     d = other.d;
72     return *this;
73 }
74 
operator =(ExtractorDocumentNode && other)75 ExtractorDocumentNode& ExtractorDocumentNode::operator=(ExtractorDocumentNode &&other)
76 {
77     if (d && d.use_count() == 1 && d->processor) {
78         d->processor->destroyNode(*this);
79     }
80     d = std::move(other.d);
81     return *this;
82 }
83 
isNull() const84 bool ExtractorDocumentNode::isNull() const
85 {
86     return d->content.isNull() || !d->processor || d->mimeType.isEmpty();
87 }
88 
parent() const89 ExtractorDocumentNode ExtractorDocumentNode::parent() const
90 {
91     return ExtractorDocumentNode(d->parent.lock());
92 }
93 
setParent(const ExtractorDocumentNode & parent)94 void ExtractorDocumentNode::setParent(const ExtractorDocumentNode &parent)
95 {
96     d->parent = parent.d;
97 }
98 
mimeType() const99 QString ExtractorDocumentNode::mimeType() const
100 {
101     return d->mimeType;
102 }
103 
setMimeType(const QString & mimeType)104 void ExtractorDocumentNode::setMimeType(const QString &mimeType)
105 {
106     d->mimeType = mimeType;
107 }
108 
content() const109 QVariant ExtractorDocumentNode::content() const
110 {
111     return d->content;
112 }
113 
setContent(const QVariant & content)114 void ExtractorDocumentNode::setContent(const QVariant &content)
115 {
116     d->content = content;
117 }
118 
processor() const119 const ExtractorDocumentProcessor* ExtractorDocumentNode::processor() const
120 {
121     return d->processor;
122 }
123 
setProcessor(const ExtractorDocumentProcessor * processor)124 void ExtractorDocumentNode::setProcessor(const ExtractorDocumentProcessor *processor)
125 {
126     assert(!d->processor);
127     d->processor = processor;
128 }
129 
childNodes() const130 const std::vector<ExtractorDocumentNode>& ExtractorDocumentNode::childNodes() const
131 {
132     return d->childNodes;
133 }
134 
appendChild(ExtractorDocumentNode & child)135 void ExtractorDocumentNode::appendChild(ExtractorDocumentNode &child)
136 {
137     if (child.isNull()) {
138         return;
139     }
140     child.setParent(*this);
141     d->childNodes.push_back(child);
142 }
143 
result() const144 ExtractorResult ExtractorDocumentNode::result() const
145 {
146     return d->result;
147 }
148 
addResult(ExtractorResult && result)149 void ExtractorDocumentNode::addResult(ExtractorResult &&result)
150 {
151     d->result.append(std::move(result));
152 }
153 
setResult(ExtractorResult && result)154 void ExtractorDocumentNode::setResult(ExtractorResult &&result)
155 {
156     d->result = std::move(result);
157 }
158 
contextDateTime() const159 QDateTime ExtractorDocumentNode::contextDateTime() const
160 {
161     if (!d->contextDateTime.isValid() && !d->parent.expired()) {
162         return parent().contextDateTime();
163     }
164     return d->contextDateTime;
165 }
166 
setContextDateTime(const QDateTime & contextDateTime)167 void ExtractorDocumentNode::setContextDateTime(const QDateTime &contextDateTime)
168 {
169     d->contextDateTime = contextDateTime;
170 }
171 
location() const172 QVariant ExtractorDocumentNode::location() const
173 {
174     if (d->location.isNull() && !d->parent.expired()) {
175         return parent().location();
176     }
177     return d->location;
178 }
179 
setLocation(const QVariant & location)180 void ExtractorDocumentNode::setLocation(const QVariant &location)
181 {
182     d->location = location;
183 }
184 
jsonLdResult() const185 QJsonArray ExtractorDocumentNode::jsonLdResult() const
186 {
187     return d->result.jsonLdResult();
188 }
189 
childNodesVariant() const190 QVariantList ExtractorDocumentNode::childNodesVariant() const
191 {
192     QVariantList l;
193     l.reserve(d->childNodes.size());
194     std::transform(d->childNodes.begin(), d->childNodes.end(), std::back_inserter(l), [](const auto &c) { return QVariant::fromValue(c); });
195     return l;
196 }
197 
contentJsValue() const198 QJSValue ExtractorDocumentNode::contentJsValue() const
199 {
200     if (!d || !d->processor) {
201         return {};
202     }
203     if (auto jsEngine = d->jsEngine()) {
204         return d->processor->contentToScriptValue(*this, jsEngine);
205     }
206     return {};
207 }
208 
setScriptEngine(QJSEngine * jsEngine) const209 void ExtractorDocumentNode::setScriptEngine(QJSEngine* jsEngine) const
210 {
211     if (!d->parent.expired()) {
212         parent().setScriptEngine(jsEngine);
213     } else {
214         d->m_jsEngine = jsEngine;
215     }
216 }
217 
findChildNodes(const QJSValue & jsFilter) const218 QVariantList ExtractorDocumentNode::findChildNodes(const QJSValue &jsFilter) const
219 {
220     const auto filter = ExtractorFilter::fromJSValue(jsFilter);
221     std::vector<ExtractorDocumentNode> matches;
222     filter.allMatches(*this, matches);
223 
224     QVariantList l;
225     l.reserve(matches.size());
226     std::transform(matches.begin(), matches.end(), std::back_inserter(l), [](const auto &c) { return QVariant::fromValue(c); });
227     return l;
228 }
229 
230 #include "moc_extractordocumentnode.cpp"
231