Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
DOM.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. 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::Exception<runtime_error> ("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 auto Element::Ptr::ImportNode (const Node::Ptr& cloneFrom) -> Ptr
230 {
231 return GetRep ()->AppendImportedNode (cloneFrom);
232 }
233 inline auto Element::Ptr::ImportNode (const Node::Ptr& cloneFrom, const Node::Ptr& afterNode) -> Ptr
234 {
235 return GetRep ()->InsertImportedNode (cloneFrom, afterNode);
236 }
237 inline Element::Ptr Element::Ptr::Append (const NameWithNamespace& eltName, const String& v)
238 {
239 auto r = Append (eltName);
240 r.SetValue (v);
241 return r;
242 }
243 template <same_as<VariantValue> VV>
244 inline Element::Ptr Element::Ptr::Append (const NameWithNamespace& eltName, const VV& v)
245 {
246 auto r = Append (eltName);
247 r.SetValue (v.template As<String> ());
248 return r;
249 }
250 inline Element::Ptr Element::Ptr::AppendIf (const NameWithNamespace& eltName, const optional<String>& v)
251 {
252 if (v) {
253 auto r = Append (eltName);
254 r.SetValue (*v);
255 return r;
256 }
257 return Element::Ptr{nullptr};
258 }
259 template <same_as<VariantValue> VV>
260 inline Element::Ptr Element::Ptr::AppendIf (const NameWithNamespace& eltName, const VV& v)
261 {
262 if (v != nullptr) {
263 return Append (eltName, v.template As<String> ());
264 }
265 return Element::Ptr{nullptr};
266 }
268 {
269 /**
270 * Basic algorithm:
271 * o If Root of document (no parent node)
272 * o Delete this
273 * o Append (this loses relative position of nodes, which we may want to fix, but should generally work OK)
274 * o If middling (not root) node
275 * o Add new node just after this one (with same name/namespace)
276 * o Delete this node (one just 'replaced')
277 * Note - to avoid issues with temporarily having two nodes of the same name in list of children - may need to modify this logic?
278 *
279 */
280 Element::Ptr parent = this->GetParent ();
281 Element::Ptr newNode{nullptr};
282 if (parent == nullptr) {
283 this->Delete ();
284 // 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
285 newNode = parent.Append (newEltName);
286 }
287 else {
288 newNode = parent.Insert (newEltName, *this);
289 this->Delete ();
290 }
291 Ensure (newNode.GetParent () == parent);
292 *this = newNode;
293 return newNode;
294 }
296 {
297 return Element::Ptr{GetRep ()->GetParentNode ()};
298 }
299 inline auto Element::Ptr::GetChildNodes () const -> Iterable<Node::Ptr>
300 {
301 AssertNotNull (GetRep ());
302 return GetRep ()->GetChildren ();
303 }
305 {
306 AssertNotNull (GetRep ());
307 return GetRep ()->GetChildren ().Map<Iterable<Ptr>> ([] (Node::Ptr p) -> optional<Ptr> {
308 Ptr eltNode{p};
309 return eltNode == nullptr ? optional<Ptr>{} : eltNode;
310 });
311 }
312 inline auto Element::Ptr::GetChildByID (const String& id) const -> Ptr
313 {
314 return Element::Ptr{GetRep ()->GetChildElementByID (id)};
315 }
317 {
318 return Element::Ptr{LookupOneNode (e)};
319 }
321 {
322 Require (e.GetOptions ().fResultTypeIndex == XPath::ResultTypeIndex_v<Node::Ptr>);
323 if (optional<XPath::Result> o = GetRep ()->LookupOne (e)) {
324 return get<Node::Ptr> (*o);
325 }
326 return Node::Ptr{};
327 }
329 {
330 return GetRep ()->Lookup (e);
331 }
333 {
334 return GetRep ()->Lookup (e).Map<Traversal::Iterable<Element::Ptr>> ([] (const XPath::Result& e) {
335 Element::Ptr ep{e};
336 return ep != nullptr ? ep : optional<Element::Ptr>{};
337 });
338 }
339 inline auto Element::Ptr::GetRep () const -> shared_ptr<IRep>
340 {
341 auto r = PeekRep ();
342 EnsureNotNull (r);
343 return r;
344 }
345 inline auto Element::Ptr::PeekRep () const -> shared_ptr<IRep>
346 {
347 return dynamic_pointer_cast<IRep> (Node::Ptr::PeekRep ());
348 }
349
350 /*
351 ********************************************************************************
352 ************************ XML::DOM::Document::Ptr *******************************
353 ********************************************************************************
354 */
355 inline Document::Ptr::Ptr (nullptr_t)
356 : fRep_{}
357 {
358 }
359 inline Document::Ptr::Ptr (const shared_ptr<IRep>& rep)
360 : fRep_{rep}
361 {
362 }
363 inline shared_ptr<Document::IRep> Document::Ptr::GetRep () const
364 {
365 RequireNotNull (fRep_);
366 return fRep_;
367 }
368 inline bool Document::Ptr::operator== (nullptr_t) const
369 {
370 return fRep_ == nullptr;
371 }
372 inline bool Document::Ptr::GetStandalone () const
373 {
374 return GetRep ()->GetStandalone ();
375 }
376 inline void Document::Ptr::SetStandalone (bool standalone)
377 {
378 GetRep ()->SetStandalone (standalone);
379 }
381 {
382 GetRep ()->Write (to, options);
383 }
385 {
386 // @todo need a better Streams DESIGN here - were we can write and produce the string directly...
387 Streams::MemoryStream::Ptr<byte> bufferedOutput = Streams::MemoryStream::New<byte> ();
388 GetRep ()->Write (bufferedOutput, options);
389 return Streams::BinaryToText::Reader::New (bufferedOutput).ReadAll ();
390 }
392 {
393 return GetRep ()->GetChildren ();
394 }
396 {
397 // Should only be one in an XML document.
398 for (Node::Ptr ni : GetRep ()->GetChildren ()) {
399 if (ni.GetNodeType () == Node::eElementNT) {
400 return ni;
401 }
402 }
403 return Element::Ptr{nullptr};
404 }
405 inline Element::Ptr Document::Ptr::ReplaceRootElement (const NameWithNamespace& newEltName, const optional<String>& namespacePrefix) const
406 {
407 // Note this cannot be implemented using the existing Replace () mechanism for elements because the document could be created without a root.
408 return GetRep ()->ReplaceRootElement (newEltName, namespacePrefix);
409 }
411 {
412 RequireNotNull (GetRootElement ());
413 return GetRootElement ().LookupOneElement (e);
414 }
416 {
417 RequireNotNull (GetRootElement ());
418 return GetRootElement ().Lookup (e);
419 }
421 {
422 RequireNotNull (GetRootElement ());
423 return GetRootElement ().LookupElements (e);
424 }
425
426}
#define AssertNotNull(p)
Definition Assertions.h:336
#define EnsureNotNull(p)
Definition Assertions.h:343
#define RequireNotNull(p)
Definition Assertions.h:350
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual void Write(const Streams::OutputStream::Ptr< byte > &to, const SerializationOptions &options={}) const
The document as XML: to a byte stream as UTF-8, with no byte order mark - or as a String.
Definition DOM.inl:380
nonvirtual Iterable< Node::Ptr > GetChildren() const
Definition DOM.inl:391
nonvirtual Traversal::Iterable< Element::Ptr > LookupElements(const XPath::Expression &e) const
shorthand for GetRootElement ().LookupElements (e) - so requires root element exists
Definition DOM.inl:420
nonvirtual shared_ptr< IRep > GetRep() const
Definition DOM.inl:363
nonvirtual Element::Ptr GetRootElement() const
always returns Node of eElement or return nullptr if none
Definition DOM.inl:395
nonvirtual Element::Ptr LookupOneElement(const XPath::Expression &e) const
shorthand for GetRootElement ().LookupOneElement (e) - so requires root element exists
Definition DOM.inl:410
nonvirtual Traversal::Iterable< XPath::Result > Lookup(const XPath::Expression &e) const
shorthand for GetRootElement ().Lookup (e) - so requires root element exists
Definition DOM.inl:415
nonvirtual Element::Ptr ReplaceRootElement(const NameWithNamespace &newEltName, const optional< String > &namespacePrefix=nullopt) const
Make this the document's root element - and say HOW its namespace is declared on it,...
Definition DOM.inl:405
nonvirtual Node::Ptr LookupOneNode(const XPath::Expression &e) const
Definition DOM.inl:320
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:312
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:267
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:250
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:304
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:299
nonvirtual Ptr ImportNode(const Node::Ptr &cloneFrom)
Append a deep CLONE of 'cloneFrom' - an element of any document - to this element's children.
nonvirtual bool HasAttribute(const NameWithNamespace &attrName) const
Definition DOM.inl:137
Node::Ptr is a smart pointer to a Node::IRep.
Definition DOM.h:243
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
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
Definition Exceptions.h:219
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:234
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)
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,...
Definition Throw.inl:54
Ptr New(const InputStream::Ptr< byte > &src, optional< SeekableFlag > seekable={}, ReadAhead readAhead=eReadAheadAllowed)
Create an InputStream::Ptr<Character> from the arguments (usually binary source) - which can be used ...
An element's or attribute's name (local-name, not including a prefix), plus the namespace that name i...
Definition Namespace.h:63