Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
DOM.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_DataExchange_XML_DOM_h_
5#define _Stroika_Foundation_DataExchange_XML_DOM_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <variant>
10
12#include "Stroika/Foundation/DataExchange/BadFormatException.h"
14#include "Stroika/Foundation/DataExchange/XML/Namespace.h"
15#include "Stroika/Foundation/Execution/Exceptions.h"
18
19namespace Stroika::Foundation::DataExchange::XML::Schema {
20 class Ptr;
21}
22namespace Stroika::Foundation::DataExchange::XML::Providers {
23 struct IDOMProvider;
24};
25
26namespace Stroika::Foundation::DataExchange::XML::DOM {
27
28 using Traversal::Iterable;
29
30 /*
31 * Special Note about namespaces:
32 *
33 * o Default namespace does is inherited by enclosed elements, but **not** by enclosed attributes.
34 *
35 * The reason this basic fact is worthy of note is:
36 * o IMHO, it is not intuitive
37 * o https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ms754539(v=vs.85) says
38 * "All elements and attributes in the document that do not have a prefix will then belong to the default namespace"
39 * THIS IS WRONG - and the top google search hit when searching about default xml namespaces.
40 *
41 * The reason I believe my counter-claim above is:
42 * o https://www.w3.org/TR/xml-names/#defaulting
43 * "The namespace name for an unprefixed attribute name always has no value" (after saying the reverse for element names)
44 */
45
46 /**
47 * \note - some serializers (xerces) may ignore fIndent
48 */
50 bool fPrettyPrint{false};
51 optional<unsigned int> fIndent{};
52 };
53
54 namespace Node {
55 struct IRep;
56 class Ptr;
57 }
58
59 namespace Element {
60 class Ptr;
61 }
62
63 namespace XPath {
64
65 /**
66 * Notes about XPath support:
67 *
68 * Tutorial/Syntax:
69 * https://www.w3schools.com/xml/xpath_syntax.asp
70 *
71 * Xerces Limitations (as of Xerces 3.2 - 2024-01)
72 * o No [] support (so p/n[@f='3'] not supported, for example)
73 */
74
75 /**
76 * For example, Xerces 3.2 doesn't support [] in expressions
77 */
82 inline const XPathExpressionNotSupported XPathExpressionNotSupported::kThe;
83
84 /**
85 * \note the default value is an empty (nullptr) Node::Ptr
86 */
87 using Result = variant<Node::Ptr, bool, int, double, String>;
88
89 /**
90 * Some APIs (like Expression) need to know the index - not just the type itself, and this maps.
91 */
92 template <typename T>
93 constexpr uint8_t ResultTypeIndex_v = static_cast<uint8_t> (Common::VariantIndex<Result, T>);
94
95 /**
96 * Expression is provider independent, but since it is implemented with a shared_ptr and immutable,
97 * the providers can maintain a cache of mappings of expressions to internal compiled version of expression.
98 *
99 * So - it behaves rather obviously - except that if you copy an expression, you KNOW the underlying data wont
100 * change so can cache derived values (like a compiled expression).
101 *
102 * \note As of 2024-01-15, LibXML2 XPath doesn't support default namespace for XPath, so use explicit namespace prefixes.
103 */
105 public:
106 /**
107 */
108 struct Options {
109 /**
110 * prefixes available to use in expression
111 */
112 NamespaceDefinitions fNamespaces;
113
114 /**
115 * index into 'Result' variant expected. REJECT other values - required by Xerces XPATH API, defaults to Node::Ptr
116 */
117 optional<uint8_t> fResultTypeIndex{ResultTypeIndex_v<Node::Ptr>};
118
119 /**
120 * Caller cares (or does not) about order of elements returned (not sure what ordering is defined - depth first or breadth first?)
121 */
122 bool fOrdered{false};
123
124 /**
125 * Snapshot less efficient, but safer in light of modifications to the node tree (but still not clearly defined what modifications allowed and what not?).
126 */
127 bool fSnapshot{false};
128
129 /**
130 */
131 nonvirtual String ToString () const;
132 };
133 static inline const Options kDefaultOptions{
134 .fNamespaces = {}, .fResultTypeIndex = ResultTypeIndex_v<Node::Ptr>, .fOrdered = false, .fSnapshot = false};
135
136 public:
137 Expression (String&& e, const Options& o);
138 template <Characters::IConvertibleToString ST>
139 Expression (ST&& e, const Options& o = kDefaultOptions);
140
141 public:
142 struct IRep {
143 virtual ~IRep () = default;
144 virtual String GetExpression () const = 0;
145 virtual Options GetOptions () const = 0;
146 };
147
148 public:
149 /**
150 * The text of the expression
151 */
152 nonvirtual String GetExpression () const;
153
154 public:
155 /**
156 * Get options (like prefix to namespace map, etc)
157 */
158 nonvirtual Options GetOptions () const;
159
160 public:
161 /**
162 */
163 nonvirtual String ToString () const;
164
165 public:
166 nonvirtual shared_ptr<const IRep> GetRep () const;
167
168 private:
169 shared_ptr<const IRep> fRep_;
170 };
171
172 }
173
174 /**
175 * NB: A Node can be EITHER an ELEMENT or an ATTRIBUTE (mostly). Nodes are 'internal' to a Document, and are always somehow contained in some document).
176 * Nodes have an abstract IRep, the the Ptr object is just a 'smart pointer' to that IRep.
177 *
178 * Note - the IRep can be implemented by a variety of backend libraries (dubbed 'Providers').
179 *
180 * Principally, users will interact with a Document::Ptr (initialized from Document::New()), and only rarely deal with Nodes.
181 * But Nodes can be accessed, created, update etc, once you start with a Document::Ptr.
182 */
183 namespace Node {
184
185 /**
186 * Prior to Stroika v3.0d5, this was Node::NodeType
187 */
188 enum Type {
189 eAttributeNT,
190 eElementNT,
191 eTextNT,
192 eCommentNT,
193 eOtherNT
194 };
195
196 /**
197 * \brief Node::Ptr is a smart pointer to a Node::IRep
198 *
199 * \note Before Stroika v3.0d5 this was simply called "Node", and now Node::Ptr
200 *
201 * Nodes are not created directly, but either via Node::Ptr methods, or Document::Ptr methods (because nodes are always associated with some document).
202 *
203 * Node::Ptr maybe nullptr (default constructed, assigned nullptr, or rendered null by a call to DeleteNode). Calling other methods (like GetName () etc)
204 * while nullptr is a requires failure.
205 *
206 * \note Older XMLDB InsertNode/AppendNode APIs - we had some APIs which more generally operated on Nodes adding them and probably allowed moving them.
207 * I couldn't think of any cases where I needed that, and it made it harder to port to other libraries, so I removed those APIs (til I see there utility again).
208 * And then - need to better document just what they do/are for (so can do portable).
209 */
210 class Ptr {
211 public:
212 /**
213 */
214 Ptr () = default;
215 Ptr (nullptr_t);
216 Ptr (const shared_ptr<IRep>& from);
217
218 public:
219 /**
220 * \note a bit queer, but very helpful - two Node::Ptr's are equal not by comparing their pointers (shared_ptr rep) but
221 * if they refer to the same underlying (in providers representation) object. Meaning if they are created by two
222 * different APIs (say an interator and some other api) - you can check to see if they refer to the same underlying node.
223 *
224 * \pre the two Ptrs are either nullptr or come from the same XML Provider object.
225 */
226 nonvirtual bool operator== (const Ptr& rhs) const;
227 nonvirtual bool operator== (nullptr_t) const;
228
229 public:
230 /*
231 * Like shared_ptr::operator bool - return true if non-null ptr
232 */
233 explicit operator bool () const;
234
235 public:
236 /**
237 */
238 nonvirtual Type GetNodeType () const;
239
240 public:
241 /**
242 * \pre GetNodeType == eAttributeNT or eElementNT
243 */
244 nonvirtual NameWithNamespace GetName () const;
245
246 public:
247 /**
248 * \pre GetNodeType == eAttributeNT or eElementNT
249 */
250 nonvirtual void SetName (const NameWithNamespace& name);
251
252 public:
253 /**
254 * This is the value between the brackets <a>text</a>. Note that <a></a> is the same as <a/> - the empty string.
255 *
256 * \note before Stroika v3.0d5, the value API was VariantValue but that was always meaningless, and just treated as a String.
257 *
258 * \note the 'String' value maybe implemented in XML in a variety of ways (entities, CDATA, etc).
259 *
260 * The /0 overload looks at 'this node'. The overload taking an XPath::Expression looks at the first selected node (using this node as context)
261 * Either way, the result is a node, and its text is returned. For XPath case, if no node is returned, the return value is nullopt.
262 *
263 * \pre GetNodeType == eAttributeNT or eElementNT
264 */
265 nonvirtual String GetValue () const;
266
267 public:
268 /**
269 * \pre GetNodeType == eAttributeNT or eElementNT
270 */
271 nonvirtual void SetValue (const String& v);
272
273 public:
274 /**
275 * \pre *this != nullptr
276 * \post *this == nullptr
277 */
278 nonvirtual void Delete ();
279
280 public:
281 /**
282 * Can return nullptr
283 */
284 nonvirtual Ptr GetParentNode () const;
285
286 public:
287 /**
288 * \brief return the associated shared_ptr (cannot be nullptr)
289 *
290 * \post result != nullptr
291 */
292 nonvirtual shared_ptr<IRep> GetRep () const;
293
294 public:
295 /**
296 * \brief return the associated shared_ptr (can be nullptr)
297 */
298 nonvirtual shared_ptr<IRep> PeekRep () const;
299
300 public:
301 /**
302 */
303 nonvirtual Characters::String ToString () const;
304
305 private:
306 shared_ptr<IRep> fRep_;
307 };
308
309 /**
310 * DOM Nodes are typically 'elements', but can also be 'text' nodes, or comments, or attributes, etc...
311 */
312 struct IRep {
313 IRep () = default;
314 virtual ~IRep () = default;
315
316 virtual const Providers::IDOMProvider* GetProvider () const = 0;
317 virtual bool Equals (const IRep* rhs) const = 0;
318 virtual Type GetNodeType () const = 0;
319 // GetName () only allowed on element/attribute
320 virtual NameWithNamespace GetName () const = 0;
321 // SetName () only allowed on element/attribute
322 virtual void SetName (const NameWithNamespace& name) = 0;
323 // GetValue () only allowed on element/attribute
324 virtual String GetValue () const = 0;
325 // SetValue () only allowed on element/attribute
326 virtual void SetValue (const String& v) = 0;
327 virtual void DeleteNode () = 0;
328 virtual Ptr GetParentNode () const = 0;
329 virtual void Write (const Streams::OutputStream::Ptr<byte>& to, const SerializationOptions& options) const = 0;
330 };
331 }
332
333 namespace Element {
334
335 using namespace Node;
336
337 struct IRep;
338
339 /**
340 * Simple wrapper on Node::Ptr API, but with the Ptr objects refer to Elements.
341 *
342 * If you assign a non-Element to an Element::Ptr, it automatically, silently becomes nullptr (like dynamic_cast).
343 *
344 * \note Giving a namespace to an element is done by giving an xmlns=XXX attribute, so the namespace
345 * is inherited by all children
346 *
347 * TODO:
348 * \todo Maybe add GetNamespaceDefinitions () to return a NamespaceDefinitionsList object with the prefixes
349 * default, and namespace URIs for this particular element. But so far no need.
350 */
351 class Ptr : public Node::Ptr {
352 public:
353 /**
354 * for XPath::Result overload, require XPath produced right type (not string etc) - but allow for nullptr
355 */
356 Ptr () = default;
357 Ptr (nullptr_t);
358 Ptr (const Node::Ptr& p);
359 Ptr (const XPath::Result& p);
360 Ptr (const shared_ptr<IRep>& p);
361
362 public:
363 /**
364 * returns string value of attribute, and nullopt if doesn't exist
365 *
366 * \pre *this != nullptr
367 */
368 nonvirtual optional<String> GetAttribute (const NameWithNamespace& attrName) const;
369
370 public:
371 /**
372 * return true iff attribute exists on this node
373 * return true iff attribute exists on this node and equals (case sensitive) value
374 *
375 * \pre *this != nullptr
376 */
377 nonvirtual bool HasAttribute (const NameWithNamespace& attrName) const;
378 nonvirtual bool HasAttribute (const NameWithNamespace& attrName, const String& value) const;
379
380 public:
381 /**
382 * Note - setting the attribute to nullopt is equivalent to deleting the attribute
383 *
384 * The 'VariantValue' overload maps null to missing attribute, and any other value to a String (v.As<String>()).
385 *
386 * \pre *this != nullptr
387 */
388 nonvirtual void SetAttribute (const NameWithNamespace& attrName, const optional<String>& v);
389 template <same_as<VariantValue> VV>
390 nonvirtual void SetAttribute (const NameWithNamespace& attrName, const VV& v);
391
392 public:
393 /**
394 * \pre *this != nullptr
395 */
396 nonvirtual optional<String> GetID () const;
397
398 public:
399 /**
400 * Gets the XML xmlns= attribute associated with THIS element. This is the default namespace used for this element
401 * and all children. If its missing (even if a parent element has a default namespace) - this will return nullopt.
402 */
403 nonvirtual optional<URI> GetDefaultNamespace () const;
404
405 public:
406 /**
407 * \brief Sets the xmlns attribute of this element
408 *
409 * \see GetDefaultNamespace
410 */
411 nonvirtual void SetDefaultNamespace (const optional<URI> defaultNS = nullopt);
412
413 public:
414 /**
415 * This is the value between the brackets <a>text</a>. Note that <a></a> is the same as <a/> - the empty string.
416 *
417 * \note before Stroika v3.0d5, the value API was VariantValue but that was always meaningless, and just treated as a String.
418 *
419 * \note the 'String' value maybe implemented in XML in a variety of ways (entities, CDATA, etc).
420 *
421 * The /0 overload looks at 'this node'. The overload taking an XPath::Expression looks at the first selected node (using this node as context)
422 * Either way, the resulting eAttributeNT or eElementNT nodes value.
423 *
424 * \pre *this != nullptr
425 */
427 nonvirtual optional<String> GetValue (const XPath::Expression& e) const;
428
429 public:
430 /**
431 * \pre e is an expression resulting in DOM nodes (could be attributes), and then get its text, and add that to an Iterable. Ignores nodes in result set other than attribute/element (like comment nodes)
432 *
433 * \note COULD have usefully made this return a Set<> since that's the most common case, but iterable is about as good, and you might
434 * plausibly want to track dup results, or other???
435 *
436 * \pre *this != nullptr
437 */
438 nonvirtual Traversal::Iterable<String> GetValues (const XPath::Expression& e) const;
439
440 public:
441 /**
442 * For now, SetValue(e,v) - must finds an Element (or Attribute) node at 'e' - otherwise throws runtime exception,
443 * cuz no way to know in general where to add what element (could do in some specific cases maybe - like simple QName or @QName expression).
444 *
445 * The overloads with VariantValue are just a short-hand for v.As<String> () on the variant value (with no checking).
446 *
447 * \pre *this != nullptr
448 */
450 nonvirtual void SetValue (const XPath::Expression& e, const String& v);
451 template <same_as<VariantValue> VV>
452 nonvirtual void SetValue (const VV& v);
453 template <same_as<VariantValue> VV>
454 nonvirtual void SetValue (const XPath::Expression& e, const VV& v);
455
456 public:
457 /**
458 * \brief Insert Element (after argument node) inside of this 'Element'
459 *
460 * if afterNode is nullptr - then this is PREPEND, else require afterNode is a member of 'Node::Ptr::GetChildren()' - need not be an element
461 *
462 * \pre *this != nullptr
463 */
464 nonvirtual Ptr Insert (const NameWithNamespace& eltName, const Node::Ptr& afterNode);
465
466 public:
467 /**
468 * value overload just creates the node and calls SetValue() before returning it (so simple shorthand).
469 *
470 * value=VariantValue overload is just a short-hand for value.As<String>() - so will fail if value cannot be converted to a String
471 *
472 * \pre *this != nullptr
473 */
474 nonvirtual Ptr Append (const NameWithNamespace& eltName);
475 nonvirtual Ptr Append (const NameWithNamespace& eltName, const String& value);
476 template <same_as<VariantValue> VV>
477 nonvirtual Ptr Append (const NameWithNamespace& eltName, const VV& value);
478
479 public:
480 /**
481 * \brief Trivial wrapper on AppendElement, but if v is missing then this is a no-op.
482 *
483 * Trivial, but I've found helpful in making certain uses more terse.
484 *
485 * VariantValue overload maps nullptr/nullopt=v to 'missing' case above, but otherwise calls v.As<String>() which may fail depending on the type of v.
486 *
487 * \pre *this != nullptr
488 */
489 nonvirtual Ptr AppendIf (const NameWithNamespace& eltName, const optional<String>& v);
490 template <same_as<VariantValue> VV>
491 nonvirtual Ptr AppendIf (const NameWithNamespace& eltName, const VV& v);
492
493 public:
494 [[deprecated]] Ptr AppendIfNotEmpty (const NameWithNamespace& eltName, const optional<String>& v)
495 {
496 return AppendIf (eltName, v == nullopt or v->empty () ? nullopt : v);
497 }
498
499 public:
500 /**
501 * creates a new (empty) node with same argument name (and namespace, and in the same position
502 *
503 * This can be used to delete all the children/content under a given node and is equivalent, except that it returns
504 * a new NodePtr, and invalidates this.
505 *
506 * Note - this CAN be used to replace the document root (same as Document::Ptr{}.ReplaceRootElement()).
507 *
508 * \pre *this != nullptr
509 */
510 nonvirtual Ptr Replace (const NameWithNamespace& newEltName);
511
512 public:
513 /**
514 * Can return nullptr
515 *
516 * \pre *this != nullptr
517 */
518 nonvirtual Ptr GetParent () const;
519
520 public:
521 /**
522 * note only returns sub-elements, so use Node::Ptr (inherited) GetChildren to get them all
523 *
524 * \warning Any modification of the DOM may invalidate live iterators or iterables, so re-fetch after each change
525 *
526 * \pre *this != nullptr
527 */
528 nonvirtual Iterable<Node::Ptr> GetChildNodes () const;
529
530 public:
531 /**
532 * note only returns sub-elements, so use Node::Ptr (inherited) GetChildren to get them all
533 *
534 * \warning Any modification of the DOM may invalidate live iterators or iterables, so re-fetch after each change
535 *
536 * \pre *this != nullptr
537 */
538 nonvirtual Iterable<Ptr> GetChildElements () const;
539
540 public:
541 /**
542 * can return a NULL Node Ptr if not found. Only examines this node's (direct) children elements (not attributes)
543 *
544 * \pre *this != nullptr
545 */
546 nonvirtual Ptr GetChild (const NameWithNamespace& eltName) const;
547
548 public:
549 /**
550 * can return a NULL Node Ptr if not found. Only examines this node's (direct) children
551 *
552 * \pre *this != nullptr
553 */
554 nonvirtual Ptr GetChildByID (const String& id) const;
555
556 public:
557 /**
558 * \note same as Lookup(), but returns 0 or 1 result, instead of iterable of all of them (so ignores e.GetOptions.fSnapshot).
559 * Often more performant and easier to use (if you know zero or one matching element).
560 *
561 * \pre *this != nullptr
562 */
564
565 public:
566 /**
567 * \note same as Lookup(), but returns 0 or 1 result, instead of iterable of all of them (so ignores e.GetOptions.fSnapshot).
568 * Often more performant and easier to use (if you know zero or one matching element).
569 *
570 * \pre *this != nullptr
571 */
572 nonvirtual Node::Ptr LookupOneNode (const XPath::Expression& e) const;
573
574 public:
575 /**
576 * PROBABLY quite unsafe to modify DOM while holding this result etc... Needs work on whehn/what is allowed.
577 * NOTE MORE SAFE IF expression uses 'snapshot'
578 *
579 * \pre *this != nullptr
580 */
582
583 public:
584 /**
585 * PROBABLY quite unsafe to modify DOM while holding this result etc... Needs work on whehn/what is allowed.
586 * NOTE MORE SAFE IF expression uses 'snapshot'
587 *
588 * \note Same as Lookup - but filters out all non-element nodes
589 *
590 * \pre *this != nullptr
591 */
593
594 public:
595 /**
596 * \brief return the associated shared_ptr (cannot be nullptr)
597 *
598 * \post result != nullptr
599 */
600 nonvirtual shared_ptr<IRep> GetRep () const;
601
602 public:
603 /**
604 * \brief return the associated shared_ptr (can be nullptr)
605 */
606 nonvirtual shared_ptr<IRep> PeekRep () const;
607 };
608
609 /**
610 * Elements are special nodes, that may contain sub-nodes.
611 */
612 struct IRep : virtual Node::IRep {
613 virtual optional<String> GetAttribute (const NameWithNamespace& attrName) const = 0;
614 virtual void SetAttribute (const NameWithNamespace& attrName, const optional<String>& v) = 0;
615 /**
616 * if afterNode is nullptr - then this is PREPEND, else require afterNode is a member of 'GetChildren()'
617 */
618 virtual Ptr InsertElement (const NameWithNamespace& eltName, const Ptr& afterNode) = 0;
619 virtual Ptr AppendElement (const NameWithNamespace& eltName) = 0;
620 virtual Iterable<Node::Ptr> GetChildren () const = 0;
621 // Redundant API, but provided since commonly used and can be optimized
622 virtual Ptr GetChildElementByID (const String& id) const;
623 // LookupOne returns zero or one results (nullopt or XPath::Result)
624 virtual optional<XPath::Result> LookupOne (const XPath::Expression& e) = 0;
625 // Lookup returns full iterator of all results (whether live or snapshot depends on params in e)
626 virtual Traversal::Iterable<XPath::Result> Lookup (const XPath::Expression& e) = 0;
627 };
628
629 }
630
631 namespace Document {
632
633 struct IRep;
634
635 /**
636 * \brief Document::Ptr is create with Document::New, and is a smart pointer to a DOM document object.
637 *
638 * \note nullptr is allowed for Ptr
639 *
640 * \note the document object does NOT have an intrinsically associated schema object. We considered/started with such a design.
641 * but then the trouble was, what if you wanted two (say a short and long format - schema). Or what if sometimes you wanted to read
642 * with no schema (validation) and sometimes with validation? It just added alot of ambiguity (in how to interpret optional/nullptr as meaning
643 * default or none).
644 *
645 * These matters could have been resolved, but in the end it seems maximally flexible, and super clear, to just not associate the schema
646 * with the document, and just specify in as an argument to read (New) or Write (serialize) etc.
647 *
648 *
649 * todo add better nullptr_t interop - static bool convert etc...
650 */
651 class Ptr {
652 public:
653 /**
654 */
655 Ptr () = default;
656 Ptr (nullptr_t);
657 Ptr (const shared_ptr<IRep>& rep);
658 Ptr (const Ptr& from) = default;
659
660 public:
661 Ptr& operator= (const Ptr& rhs) = default;
662
663 public:
664 ~Ptr () = default;
665
666 public:
667 /**
668 */
669 bool operator== (const Ptr&) const = default;
670 bool operator== (nullptr_t) const;
671
672 public:
673 nonvirtual bool GetStandalone () const;
674
675 public:
676 nonvirtual void SetStandalone (bool standalone);
677
678 public:
679 /**
680 */
681 nonvirtual void Write (const Streams::OutputStream::Ptr<byte>& to, const SerializationOptions& options = {}) const;
682 nonvirtual String Write (const SerializationOptions& options = {}) const;
683
684 public:
685 /**
686 * throws BadFormatException exception on failure
687 */
688 nonvirtual void Validate (const Schema::Ptr& schema) const;
689
690 public:
691 /**
692 * Return (and iterable) of nodes, which represent the top level children of the document. Typically there will be one interesting - the
693 * root element, so calling GetRootElement() will typically be easier/more useful (so as to not see/get confused with things like comments, or processing directives).
694 */
695 nonvirtual Iterable<Node::Ptr> GetChildren () const;
696
697 public:
698 /**
699 * \brief always returns Node of eElement or return nullptr if none
700 */
701 nonvirtual Element::Ptr GetRootElement () const;
702
703 public:
704 /**
705 * If there exists a root element, this is the same as GetRootElement ().Replace (arg), except for the childrenInheritNS part)
706 *
707 * \note this creates an xmlns=NS attribute on the root element if a namespace is given in newEltName, so that the namespace
708 * is inherited by subelements.
709 */
710 nonvirtual Element::Ptr ReplaceRootElement (const NameWithNamespace& newEltName, bool childrenInheritNS = true) const;
711
712 public:
713 /**
714 * \brief shorthand for GetRootElement ().LookupOneElement (e) - so requires root element exists
715 */
716 nonvirtual Element::Ptr LookupOneElement (const XPath::Expression& e) const;
717
718 public:
719 /**
720 * \brief shorthand for GetRootElement ().Lookup (e) - so requires root element exists
721 */
723
724 public:
725 /**
726 * \brief shorthand for GetRootElement ().LookupElements (e) - so requires root element exists
727 */
729
730 public:
731 /**
732 */
733 nonvirtual Characters::String ToString () const;
734
735 public:
736 /**
737 * @@todo - what happens if we rename this operator-> - IO think chaining works nicely then. maybe use that trick throughout Stroika if it works here.
738 */
739 nonvirtual shared_ptr<IRep> GetRep () const;
740
741 private:
742 shared_ptr<IRep> fRep_;
743 };
744
745 /**
746 * Create a Document object and return a smart pointer (Ptr) to it.
747 *
748 * Use the optionally provided stream to deserialize the document from (or create an empty one with a single root documentElement given by name/ns args).
749 *
750 * \note String in overload is trivial wrapper on Streams::TextToByteReader{}, in case you want adjust parameters.
751 * \note Document::Ptr overload is a way to 'clone' a Document (but even possibly across provider implementations)
752 * \note 'in' stream may be null to create a document with no root element (why is this useful?).
753 *
754 * @todo add overload taking String 'in' and parse using Streams::TextToByteReader
755 * @todo consider adding 'Resolver' argument - so missing #includes get loaded.
756 *
757 * If not otherwise specified (e..g if not read from stream where specified otherwise), XML documents default to standalone (=yes)
758 */
759 Ptr New (const Providers::IDOMProvider& p);
760 Ptr New (const Providers::IDOMProvider& p, const NameWithNamespace& documentElementName);
761 Ptr New (const Providers::IDOMProvider& p, const Streams::InputStream::Ptr<byte>& in);
762 Ptr New (const Providers::IDOMProvider& p, const Streams::InputStream::Ptr<byte>& in, const Schema::Ptr& schemaToValidateAgainstWhileReading);
763 Ptr New (const Providers::IDOMProvider& p, const String& in);
764 Ptr New (const Providers::IDOMProvider& p, const String& in, const Schema::Ptr& schemaToValidateAgainstWhileReading);
765 Ptr New (const Providers::IDOMProvider& p, const Ptr& clone);
766#if qStroika_Foundation_DataExchange_XML_SupportDOM
767 Ptr New ();
768 Ptr New (const NameWithNamespace& documentElementName);
769 Ptr New (const Streams::InputStream::Ptr<byte>& in);
770 Ptr New (const Streams::InputStream::Ptr<byte>& in, const Schema::Ptr& schemaToValidateAgainstWhileReading);
771 Ptr New (const String& in);
772 Ptr New (const String& in, const Schema::Ptr& schemaToValidateAgainstWhileReading);
773 Ptr New (const Ptr& clone);
774#endif
775
776 /**
777 */
778 struct IRep {
779 virtual ~IRep () = default;
780 virtual const Providers::IDOMProvider* GetProvider () const = 0;
781 virtual bool GetStandalone () const = 0;
782 virtual void SetStandalone (bool standalone) = 0;
783 virtual Element::Ptr ReplaceRootElement (const NameWithNamespace& newEltName, bool childrenInheritNS) = 0;
784 virtual void Write (const Streams::OutputStream::Ptr<byte>& to, const SerializationOptions& options) const = 0;
785 virtual Iterable<Node::Ptr> GetChildren () const = 0;
786 virtual void Validate (const Schema::Ptr& schema) const = 0;
787 };
788
789 }
790
791}
792
793/*
794 ********************************************************************************
795 ***************************** Implementation Details ***************************
796 ********************************************************************************
797 */
798
799#include "DOM.inl"
800
801#endif /*_Stroika_Foundation_DataExchange_XML_DOM_h_*/
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
Document::Ptr is create with Document::New, and is a smart pointer to a DOM document object.
Definition DOM.h:651
nonvirtual void Validate(const Schema::Ptr &schema) const
Definition DOM.cpp:149
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 Append(const NameWithNamespace &eltName)
Definition DOM.inl:225
nonvirtual Element::Ptr LookupOneElement(const XPath::Expression &e) const
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 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 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
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
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
virtual Ptr InsertElement(const NameWithNamespace &eltName, const Ptr &afterNode)=0
used to specify default namespace, and any n: prefixes applicable to elements.
Definition Namespace.h:27