Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
DOM.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
5#include "Stroika/Foundation/Streams/MemoryStream.h"
6
7namespace Stroika::Foundation::DataExchange::XML::DOM {
8
9 /*
10 ********************************************************************************
11 ************************ XML::DOM::XPath::Expression ***************************
12 ********************************************************************************
13 */
14 template <Characters::IConvertibleToString ST>
15 inline XPath::Expression::Expression (ST&& e, const Options& o)
16 : Expression{String{e}, o}
17 {
18 }
19 inline String XPath::Expression::GetExpression () const
20 {
21 return fRep_->GetExpression ();
22 }
23 inline auto XPath::Expression::GetOptions () const -> Options
24 {
25 return fRep_->GetOptions ();
26 }
27 inline auto XPath::Expression::GetRep () const -> shared_ptr<const IRep>
28 {
29 return fRep_;
30 }
31
32 /*
33 ********************************************************************************
34 ****************************** XML::DOM::Node::Ptr *****************************
35 ********************************************************************************
36 */
37 inline Node::Ptr::Ptr (const shared_ptr<IRep>& from)
38 : fRep_{from}
39 {
40 }
41 inline Node::Ptr::Ptr (nullptr_t)
42 : fRep_{}
43 {
44 }
45 inline bool Node::Ptr::operator== (const Ptr& rhs) const
46 {
47 if (fRep_ == nullptr) [[unlikely]] {
48 return rhs.fRep_ == nullptr;
49 }
50 if (rhs.fRep_ == nullptr) {
51 return false;
52 }
53 return fRep_->Equals (rhs.fRep_.get ());
54 }
55 inline bool Node::Ptr::operator== (nullptr_t) const
56 {
57 return fRep_ == nullptr;
58 }
59 inline Node::Ptr::operator bool () const
60 {
61 return fRep_.operator bool ();
62 }
63 inline Node::Type Node::Ptr::GetNodeType () const
64 {
65 RequireNotNull (fRep_);
66 return fRep_->GetNodeType ();
67 }
68 inline auto Node::Ptr::GetName () const -> NameWithNamespace
69 {
70 Require (GetNodeType () == eAttributeNT or GetNodeType () == eElementNT);
71 return GetRep ()->GetName ();
72 }
73 inline void Node::Ptr::SetName (const NameWithNamespace& name)
74 {
75 Require (GetNodeType () == eAttributeNT or GetNodeType () == eElementNT);
76 GetRep ()->SetName (name);
77 }
79 {
80 Require (GetNodeType () == eAttributeNT or GetNodeType () == eElementNT);
81 return GetRep ()->GetValue ();
82 }
83 inline void Node::Ptr::SetValue (const String& v)
84 {
85 Require (GetNodeType () == eAttributeNT or GetNodeType () == eElementNT);
86 return GetRep ()->SetValue (v);
87 }
88 inline void Node::Ptr::Delete ()
89 {
90 RequireNotNull (fRep_);
91 fRep_->DeleteNode ();
92 fRep_ = nullptr;
93 }
95 {
96 AssertNotNull (fRep_);
97 return fRep_->GetParentNode ();
98 }
99 inline shared_ptr<Node::IRep> Node::Ptr::GetRep () const
100 {
101 EnsureNotNull (fRep_);
102 return fRep_;
103 }
104 inline shared_ptr<Node::IRep> Node::Ptr::PeekRep () const
105 {
106 return fRep_;
107 }
108
109 /*
110 ********************************************************************************
111 ************************* XML::DOM::Element::Ptr *******************************
112 ********************************************************************************
113 */
114 inline Element::Ptr::Ptr (nullptr_t)
115 : Node::Ptr{nullptr}
116 {
117 }
118 inline Element::Ptr::Ptr (const Node::Ptr& p)
119 : Node::Ptr{p != nullptr and p.GetNodeType () == Node::eElementNT ? p : nullptr}
120 {
121 Require (PeekRep () == nullptr or GetNodeType () == eElementNT);
122 }
123 inline Element::Ptr::Ptr (const shared_ptr<IRep>& rep)
124 : Node::Ptr{rep}
125 {
126 Require (PeekRep () == nullptr or GetNodeType () == eElementNT);
127 }
128 inline Element::Ptr::Ptr (const XPath::Result& r)
129 : Node::Ptr{get<Node::Ptr> (r)}
130 {
131 Require (PeekRep () == nullptr or GetNodeType () == eElementNT);
132 }
133 inline optional<String> Element::Ptr::GetAttribute (const NameWithNamespace& attrName) const
134 {
135 return GetRep ()->GetAttribute (attrName);
136 }
137 inline bool Element::Ptr::HasAttribute (const NameWithNamespace& attrName) const
138 {
139 return GetRep ()->GetAttribute (attrName) != nullopt;
140 }
141 inline bool Element::Ptr::HasAttribute (const NameWithNamespace& attrName, const String& value) const
142 {
143 if (auto o = GetRep ()->GetAttribute (attrName)) {
144 return *o == value;
145 }
146 return false;
147 }
148 inline void Element::Ptr::SetAttribute (const NameWithNamespace& attrName, const optional<String>& v)
149 {
150 GetRep ()->SetAttribute (attrName, v);
151 }
152 template <same_as<VariantValue> VV>
153 inline void Element::Ptr::SetAttribute (const NameWithNamespace& attrName, const VV& v)
154 {
155 GetRep ()->SetAttribute (attrName, v == nullptr ? optional<String>{} : v.template As<String> ());
156 }
157 inline optional<String> Element::Ptr::GetID () const
158 {
159 static const NameWithNamespace kID_{"id"sv};
160 return GetRep ()->GetAttribute (kID_);
161 }
162 inline optional<URI> Element::Ptr::GetDefaultNamespace () const
163 {
164 if (auto v = GetAttribute (kXMLNS)) {
165 return URI{*v};
166 }
167 return nullopt;
168 }
169 inline void Element::Ptr::SetDefaultNamespace (const optional<URI> defaultNS)
170 {
171 SetAttribute (kXMLNS, defaultNS == nullopt ? optional<String>{} : defaultNS->As<String> ());
172 }
173 inline optional<String> Element::Ptr::GetValue (const XPath::Expression& e) const
174 {
175 Require (e.GetOptions ().fResultTypeIndex == XPath::ResultTypeIndex_v<Node::Ptr>);
176 if (optional<XPath::Result> o = GetRep ()->LookupOne (e)) {
177 Node::Ptr ee = get<Node::Ptr> (*o);
178 if (ee != nullptr) {
179 return ee.GetValue ();
180 }
181 }
182 return nullopt;
183 }
185 {
186 Require (e.GetOptions ().fResultTypeIndex == XPath::ResultTypeIndex_v<Node::Ptr>);
187 return GetRep ()->Lookup (e).Map<Iterable<String>> ([] (const XPath::Result& e) -> optional<String> {
188 Node::Ptr ep = get<Node::Ptr> (e);
189 switch (ep.GetNodeType ()) {
190 case Node::Type::eAttributeNT:
191 case Node::Type::eElementNT:
192 return ep.GetValue ();
193 default:
194 return nullopt; // skip comment nodes etc...
195 }
196 });
197 }
198 inline void Element::Ptr::SetValue (const XPath::Expression& e, const String& v)
199 {
200 Require (e.GetOptions ().fResultTypeIndex == XPath::ResultTypeIndex_v<Node::Ptr>);
201 if (optional<XPath::Result> o = GetRep ()->LookupOne (e)) {
202 Node::Ptr ee = get<Node::Ptr> (*o);
203 if (ee != nullptr) {
204 ee.SetValue (v);
205 return;
206 }
207 }
208 static const auto kException_ = Execution::RuntimeErrorException<> ("Node not found relative to given element"sv);
209 Execution::Throw (kException_);
210 }
211 template <same_as<VariantValue> VV>
212 inline void Element::Ptr::SetValue (const VV& v)
213 {
214 SetValue (v.template As<String> ());
215 }
216 template <same_as<VariantValue> VV>
217 inline void Element::Ptr::SetValue (const XPath::Expression& e, const VV& v)
218 {
219 SetValue (e, v.template As<String> ());
220 }
221 inline auto Element::Ptr::Insert (const NameWithNamespace& eltName, const Node::Ptr& afterNode) -> Ptr
222 {
223 return GetRep ()->InsertElement (eltName, afterNode);
224 }
226 {
227 return GetRep ()->AppendElement (eltName);
228 }
229 inline Element::Ptr Element::Ptr::Append (const NameWithNamespace& eltName, const String& v)
230 {
231 auto r = Append (eltName);
232 r.SetValue (v);
233 return r;
234 }
235 template <same_as<VariantValue> VV>
236 inline Element::Ptr Element::Ptr::Append (const NameWithNamespace& eltName, const VV& v)
237 {
238 auto r = Append (eltName);
239 r.SetValue (v.template As<String> ());
240 return r;
241 }
242 inline Element::Ptr Element::Ptr::AppendIf (const NameWithNamespace& eltName, const optional<String>& v)
243 {
244 if (v) {
245 auto r = Append (eltName);
246 r.SetValue (*v);
247 return r;
248 }
249 return Element::Ptr{nullptr};
250 }
251 template <same_as<VariantValue> VV>
252 inline Element::Ptr Element::Ptr::AppendIf (const NameWithNamespace& eltName, const VV& v)
253 {
254 if (v != nullptr) {
255 return Append (eltName, v.template As<String> ());
256 }
257 return Element::Ptr{nullptr};
258 }
260 {
261 /**
262 * Basic algorithm:
263 * o If Root of document (no parent node)
264 * o Delete this
265 * o Append (this loses relative position of nodes, which we may want to fix, but should generally work OK)
266 * o If middling (not root) node
267 * o Add new node just after this one (with same name/namespace)
268 * o Delete this node (one just 'replaced')
269 * Note - to avoid issues with temporarily having two nodes of the same name in list of children - may need to modify this logic?
270 *
271 */
272 Element::Ptr parent = this->GetParent ();
273 Element::Ptr newNode{nullptr};
274 if (parent == nullptr) {
275 this->Delete ();
276 // technically 'Append' is not right and we really should grab the 'next sibling' and insert before it...but that seems unlikely to matter --LGP 2024-01-06
277 newNode = parent.Append (newEltName);
278 }
279 else {
280 newNode = parent.Insert (newEltName, *this);
281 this->Delete ();
282 }
283 Ensure (newNode.GetParent () == parent);
284 *this = newNode;
285 return newNode;
286 }
288 {
289 return Element::Ptr{GetRep ()->GetParentNode ()};
290 }
291 inline auto Element::Ptr::GetChildNodes () const -> Iterable<Node::Ptr>
292 {
293 AssertNotNull (GetRep ());
294 return GetRep ()->GetChildren ();
295 }
297 {
298 AssertNotNull (GetRep ());
299 return GetRep ()->GetChildren ().Map<Iterable<Ptr>> ([] (Node::Ptr p) -> optional<Ptr> {
300 Ptr eltNode{p};
301 return eltNode == nullptr ? optional<Ptr>{} : eltNode;
302 });
303 }
304 inline auto Element::Ptr::GetChildByID (const String& id) const -> Ptr
305 {
306 return Element::Ptr{GetRep ()->GetChildElementByID (id)};
307 }
309 {
310 return Element::Ptr{LookupOneNode (e)};
311 }
313 {
314 Require (e.GetOptions ().fResultTypeIndex == XPath::ResultTypeIndex_v<Node::Ptr>);
315 if (optional<XPath::Result> o = GetRep ()->LookupOne (e)) {
316 return get<Node::Ptr> (*o);
317 }
318 return Node::Ptr{};
319 }
321 {
322 return GetRep ()->Lookup (e);
323 }
325 {
326 return GetRep ()->Lookup (e).Map<Traversal::Iterable<Element::Ptr>> ([] (const XPath::Result& e) {
327 Element::Ptr ep{e};
328 return ep != nullptr ? ep : optional<Element::Ptr>{};
329 });
330 }
331 inline auto Element::Ptr::GetRep () const -> shared_ptr<IRep>
332 {
333 auto r = PeekRep ();
334 EnsureNotNull (r);
335 return r;
336 }
337 inline auto Element::Ptr::PeekRep () const -> shared_ptr<IRep>
338 {
339 return dynamic_pointer_cast<IRep> (Node::Ptr::PeekRep ());
340 }
341
342 /*
343 ********************************************************************************
344 ************************ XML::DOM::Document::Ptr *******************************
345 ********************************************************************************
346 */
347 inline Document::Ptr::Ptr (nullptr_t)
348 : fRep_{}
349 {
350 }
351 inline Document::Ptr::Ptr (const shared_ptr<IRep>& rep)
352 : fRep_{rep}
353 {
354 }
355 inline shared_ptr<Document::IRep> Document::Ptr::GetRep () const
356 {
357 RequireNotNull (fRep_);
358 return fRep_;
359 }
360 inline bool Document::Ptr::operator== (nullptr_t) const
361 {
362 return fRep_ == nullptr;
363 }
364 inline bool Document::Ptr::GetStandalone () const
365 {
366 return GetRep ()->GetStandalone ();
367 }
368 inline void Document::Ptr::SetStandalone (bool standalone)
369 {
370 GetRep ()->SetStandalone (standalone);
371 }
372 inline void Document::Ptr::Write (const Streams::OutputStream::Ptr<byte>& to, const SerializationOptions& options) const
373 {
374 GetRep ()->Write (to, options);
375 }
376 inline String Document::Ptr::Write (const SerializationOptions& options) const
377 {
378 // @todo need a better Streams DESIGN here - were we can write and produce the string directly...
379 Streams::MemoryStream::Ptr<byte> bufferedOutput = Streams::MemoryStream::New<byte> ();
380 GetRep ()->Write (bufferedOutput, options);
381 return Streams::BinaryToText::Reader::New (bufferedOutput).ReadAll ();
382 }
384 {
385 return GetRep ()->GetChildren ();
386 }
388 {
389 // Should only be one in an XML document.
390 for (Node::Ptr ni : GetRep ()->GetChildren ()) {
391 if (ni.GetNodeType () == Node::eElementNT) {
392 return ni;
393 }
394 }
395 return Element::Ptr{nullptr};
396 }
397 inline Element::Ptr Document::Ptr::ReplaceRootElement (const NameWithNamespace& newEltName, bool childrenInheritNS) const
398 {
399 // Note this cannot be implemented using the existing Replace () mechanism for elements because the document could be created without a root.
400 return GetRep ()->ReplaceRootElement (newEltName, childrenInheritNS);
401 }
403 {
404 RequireNotNull (GetRootElement ());
405 return GetRootElement ().LookupOneElement (e);
406 }
408 {
409 RequireNotNull (GetRootElement ());
410 return GetRootElement ().Lookup (e);
411 }
413 {
414 RequireNotNull (GetRootElement ());
415 return GetRootElement ().LookupElements (e);
416 }
417
418}
#define AssertNotNull(p)
Definition Assertions.h:333
#define EnsureNotNull(p)
Definition Assertions.h:340
#define RequireNotNull(p)
Definition Assertions.h:347
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual Element::Ptr ReplaceRootElement(const NameWithNamespace &newEltName, bool childrenInheritNS=true) const
Definition DOM.inl:397
nonvirtual Iterable< Node::Ptr > GetChildren() const
Definition DOM.inl:383
nonvirtual Traversal::Iterable< Element::Ptr > LookupElements(const XPath::Expression &e) const
shorthand for GetRootElement ().LookupElements (e) - so requires root element exists
Definition DOM.inl:412
nonvirtual shared_ptr< IRep > GetRep() const
Definition DOM.inl:355
nonvirtual Element::Ptr GetRootElement() const
always returns Node of eElement or return nullptr if none
Definition DOM.inl:387
nonvirtual Element::Ptr LookupOneElement(const XPath::Expression &e) const
shorthand for GetRootElement ().LookupOneElement (e) - so requires root element exists
Definition DOM.inl:402
nonvirtual Traversal::Iterable< XPath::Result > Lookup(const XPath::Expression &e) const
shorthand for GetRootElement ().Lookup (e) - so requires root element exists
Definition DOM.inl:407
nonvirtual Node::Ptr LookupOneNode(const XPath::Expression &e) const
Definition DOM.inl:312
nonvirtual shared_ptr< IRep > PeekRep() const
return the associated shared_ptr (can be nullptr)
nonvirtual optional< String > GetID() const
nonvirtual optional< URI > GetDefaultNamespace() const
Definition DOM.inl:162
nonvirtual Traversal::Iterable< Element::Ptr > LookupElements(const XPath::Expression &e) const
nonvirtual Ptr GetChildByID(const String &id) const
Definition DOM.inl:304
nonvirtual Ptr Append(const NameWithNamespace &eltName)
Definition DOM.inl:225
nonvirtual Element::Ptr LookupOneElement(const XPath::Expression &e) const
nonvirtual Ptr Replace(const NameWithNamespace &newEltName)
Definition DOM.inl:259
nonvirtual Traversal::Iterable< XPath::Result > Lookup(const XPath::Expression &e) const
nonvirtual Ptr AppendIf(const NameWithNamespace &eltName, const optional< String > &v)
Trivial wrapper on AppendElement, but if v is missing then this is a no-op.
Definition DOM.inl:242
nonvirtual void SetDefaultNamespace(const optional< URI > defaultNS=nullopt)
Sets the xmlns attribute of this element.
Definition DOM.inl:169
nonvirtual void SetAttribute(const NameWithNamespace &attrName, const optional< String > &v)
Definition DOM.inl:148
nonvirtual optional< String > GetAttribute(const NameWithNamespace &attrName) const
Definition DOM.inl:133
nonvirtual Iterable< Ptr > GetChildElements() const
Definition DOM.inl:296
nonvirtual Ptr Insert(const NameWithNamespace &eltName, const Node::Ptr &afterNode)
Insert Element (after argument node) inside of this 'Element'.
Definition DOM.inl:221
nonvirtual shared_ptr< IRep > GetRep() const
return the associated shared_ptr (cannot be nullptr)
nonvirtual Traversal::Iterable< String > GetValues(const XPath::Expression &e) const
Definition DOM.inl:184
nonvirtual Iterable< Node::Ptr > GetChildNodes() const
Definition DOM.inl:291
nonvirtual bool HasAttribute(const NameWithNamespace &attrName) const
Definition DOM.inl:137
Node::Ptr is a smart pointer to a Node::IRep.
Definition DOM.h:210
nonvirtual void SetValue(const String &v)
Definition DOM.inl:83
nonvirtual NameWithNamespace GetName() const
Definition DOM.inl:68
nonvirtual void SetName(const NameWithNamespace &name)
Definition DOM.inl:73
nonvirtual bool operator==(const Ptr &rhs) const
Definition DOM.inl:45
nonvirtual shared_ptr< IRep > PeekRep() const
return the associated shared_ptr (can be nullptr)
Definition DOM.inl:104
nonvirtual shared_ptr< IRep > GetRep() const
return the associated shared_ptr (cannot be nullptr)
Definition DOM.inl:99
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
nonvirtual RESULT_CONTAINER Map(ELEMENT_MAPPER &&elementMapper) const
functional API which iterates over all members of an Iterable, applies a map function to each element...
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
Ptr New(const InputStream::Ptr< byte > &src, optional< AutomaticCodeCvtFlags > codeCvtFlags={}, optional< SeekableFlag > seekable={}, ReadAhead readAhead=eReadAheadAllowed)
Create an InputStream::Ptr<Character> from the arguments (usually binary source) - which can be used ...