4#include "Stroika/Foundation/StroikaPreComp.h"
8#include <libxml/xmlmemory.h>
9#include <libxml/xmlsave.h>
13#include "Stroika/Foundation/DataExchange/BadFormatException.h"
15#include "Stroika/Foundation/Execution/Throw.h"
17#include "Stroika/Foundation/Memory/Common.h"
26using namespace Stroika::Foundation::DataExchange::XML;
27using namespace Stroika::Foundation::DataExchange::XML::DOM;
28using namespace Stroika::Foundation::DataExchange::XML::Schema;
29using namespace Stroika::Foundation::DataExchange::XML::Providers::LibXML2;
30using namespace Stroika::Foundation::Debug;
32using namespace Stroika::Foundation::Streams;
39static_assert (qStroika_HasComponent_libxml2,
"Don't compile this file if qStroika_HasComponent_libxml2 not set");
50 constexpr auto kUseURIEncodingFlag_ = URI::StringPCTEncodedFlag::eDecoded;
59 struct RegisterResolver_ {
60 static inline thread_local RegisterResolver_* sCurrent_ =
nullptr;
64 : fResolver_{resolver}
67 if (resolver !=
nullptr) {
69 if (xmlRegisterInputCallbacks (ResolverMatch_, ResolverOpen_, ResolverRead_, ResolverClose_) < 0) {
85 static int ResolverMatch_ (
const char*
URI)
89 optional<Resource::Definition> r = sCurrent_->fResolver_.Lookup (
Resource::Name{
90 .fNamespace = String::FromUTF8 (
URI), .fPublicID = String::FromUTF8 (
URI), .fSystemID = String::FromUTF8 (
URI)});
92 DbgTrace (
"Note ResolveMatch {} failed to find an entry in resolver."_f, String::FromUTF8 (
URI));
94 return r.has_value ();
104 static void* ResolverOpen_ (
const char*
URI)
107 optional<Resource::Definition> r = sCurrent_->fResolver_.Lookup (
Resource::Name{
108 .fNamespace = String::FromUTF8 (
URI), .fPublicID = String::FromUTF8 (
URI), .fSystemID = String::FromUTF8 (
URI)});
111 if (r.has_value ()) {
125 static int ResolverClose_ (
void* context)
127 if (context ==
nullptr)
141 static int ResolverRead_ (
void* context,
char* buffer,
int len)
145 span<byte> r = inStream->
ReadBlocking (as_writable_bytes (span{buffer,
static_cast<size_t> (len)}));
146 return static_cast<int> (r.size ());
153 struct SchemaRep_ : ILibXML2SchemaRep {
154#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
155 static inline atomic<unsigned int> sLiveCnt{0};
158 : fResolver_{resolver}
159 , fSchemaData{schemaData.ReadAll ()}
161 RegisterResolver_ registerResolver{resolver};
162 xmlSchemaParserCtxt* schemaParseContext =
163 xmlSchemaNewMemParserCtxt (
reinterpret_cast<const char*
> (fSchemaData.data ()),
static_cast<int> (fSchemaData.size ()));
164 fCompiledSchema = xmlSchemaParse (schemaParseContext);
165 xmlSchemaFreeParserCtxt (schemaParseContext);
167#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
171 SchemaRep_ (
const SchemaRep_&) =
delete;
172 virtual ~SchemaRep_ ()
174 xmlSchemaFree (fCompiledSchema);
175#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
176 Assert (sLiveCnt > 0);
182 xmlSchema* fCompiledSchema{
nullptr};
184 virtual const Providers::ISchemaProvider* GetProvider ()
const override
186 return &XML::Providers::LibXML2::kDefaultProvider;
188 virtual optional<URI> GetTargetNamespace ()
const override
190 Assert (fCompiledSchema !=
nullptr);
191 if (fCompiledSchema->targetNamespace !=
nullptr) {
205 virtual xmlSchema* GetSchemaLibRep ()
override
208 return fCompiledSchema;
216 xmlSAXHandler flibXMLSaxHndler_{};
219 : fCallback_{callback}
221 flibXMLSaxHndler_.initialized = XML_SAX2_MAGIC;
222 flibXMLSaxHndler_.startDocument = [] (
void* ctx) {
223 SAXReader_* thisReader =
reinterpret_cast<SAXReader_*
> (ctx);
224 Assert (thisReader->flibXMLSaxHndler_.initialized == XML_SAX2_MAGIC);
225 thisReader->fCallback_.StartDocument ();
227 flibXMLSaxHndler_.endDocument = [] (
void* ctx) {
228 SAXReader_* thisReader =
reinterpret_cast<SAXReader_*
> (ctx);
229 Assert (thisReader->flibXMLSaxHndler_.initialized == XML_SAX2_MAGIC);
230 thisReader->fCallback_.EndDocument ();
232 flibXMLSaxHndler_.startElementNs = [] (
void* ctx,
const xmlChar* localname, [[maybe_unused]]
const xmlChar* prefix,
const xmlChar*
URI,
233 [[maybe_unused]]
int nb_namespaces, [[maybe_unused]]
const xmlChar** namespaces,
234 int nb_attributes, [[maybe_unused]]
int nb_defaulted,
const xmlChar** attributes) {
235 SAXReader_* thisReader =
reinterpret_cast<SAXReader_*
> (ctx);
236 Assert (thisReader->flibXMLSaxHndler_.initialized == XML_SAX2_MAGIC);
238 if (attributes !=
nullptr) {
241 auto ai = attributes;
242 for (
int i = 0; i < nb_attributes; ++i) {
249 if (
URI ==
nullptr) {
256 flibXMLSaxHndler_.endElementNs = [] (
void* ctx,
const xmlChar* localname, [[maybe_unused]]
const xmlChar* prefix,
const xmlChar*
URI) {
257 SAXReader_* thisReader =
reinterpret_cast<SAXReader_*
> (ctx);
258 if (
URI ==
nullptr) {
266 flibXMLSaxHndler_.characters = [] (
void* ctx,
const xmlChar* ch,
int len) {
267 SAXReader_* thisReader =
reinterpret_cast<SAXReader_*
> (ctx);
269 thisReader->fCallback_.TextInsideElement (String::FromUTF8 (span{
reinterpret_cast<const char*
> (ch),
static_cast<size_t> (len)}));
272 SAXReader_ (
const SAXReader_&) =
delete;
277#if qStroika_Foundation_Debug_AssertionsChecked
278 bool ValidNewNodeName_ (
const String& n)
283 if (n.
find (
':') != wstring::npos) {
293 DocRep_* GetWrapperDoc_ (xmlDoc* d);
294 DocRep_* GetWrapperDoc_ (xmlNode* n)
298 return GetWrapperDoc_ (n->doc);
300 xmlNs* GetSharedReUsableXMLNSParentNamespace_ (xmlDoc* d);
301 xmlNs* GetSharedReUsableXMLNSParentNamespace_ (xmlNode* n)
305 return GetSharedReUsableXMLNSParentNamespace_ (n->doc);
310 Node::Ptr WrapLibXML2NodeInStroikaNode_ (xmlNode* n);
316 NodeRep_ (xmlNode* n)
321 virtual const Providers::IDOMProvider* GetProvider ()
const override
323 return &Providers::LibXML2::kDefaultProvider;
325 virtual bool Equals (
const IRep* rhs)
const override
329 return fNode_ ==
dynamic_cast<const NodeRep_*
> (rhs)->fNode_;
331 virtual Node::Type GetNodeType ()
const override
334 switch (fNode_->type) {
335 case XML_ELEMENT_NODE:
336 return Node::eElementNT;
337 case XML_ATTRIBUTE_NODE:
338 return Node::eAttributeNT;
340 return Node::eTextNT;
341 case XML_COMMENT_NODE:
342 return Node::eCommentNT;
344 return Node::eOtherNT;
350 Require (GetNodeType () == Node::eElementNT or GetNodeType () == Node::eAttributeNT);
351 switch (fNode_->type) {
352 case XML_ATTRIBUTE_NODE:
353 case XML_ELEMENT_NODE: {
354 const xmlChar* ns = fNode_->ns ==
nullptr ? nullptr : fNode_->ns->href;
365#if qStroika_Foundation_Debug_AssertionsChecked
366 Require (ValidNewNodeName_ (name.fName));
368 if (name.fNamespace) {
373 xmlNodeSetName (fNode_, BAD_CAST name.fName.
AsUTF8 ().c_str ());
375 virtual String GetValue ()
const override
378 auto r = xmlNodeGetContent (fNode_);
379 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([&] ()
noexcept { xmlFree (r); });
382 virtual void SetValue (
const String& v)
override
386 bool mustEncode =
true;
388 xmlChar* p = xmlEncodeSpecialChars (fNode_->doc, BAD_CAST v.
AsUTF8 ().c_str ());
389 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([&] ()
noexcept { xmlFree (p); });
390 xmlNodeSetContent (fNode_, p);
393 xmlNodeSetContent (fNode_, BAD_CAST v.
AsUTF8 ().c_str ());
396 virtual void DeleteNode ()
override
399 xmlUnlinkNode (fNode_);
400 xmlFreeNode (fNode_);
403 virtual Node::Ptr GetParentNode ()
const override
406 if (fNode_->parent ==
nullptr) {
409 return WrapLibXML2NodeInStroikaNode_ (fNode_->parent);
413 xmlBufferPtr xmlBuf = xmlBufferCreate ();
414 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([&] ()
noexcept { xmlBufferFree (xmlBuf); });
415 if (
int dumpRes = xmlNodeDump (xmlBuf, fNode_->doc, fNode_, 0, options.fPrettyPrint); dumpRes == -1) {
418 const xmlChar* t = xmlBufferContent (xmlBuf);
420 to.
Write (span{
reinterpret_cast<const byte*
> (t),
static_cast<size_t> (::strlen (
reinterpret_cast<const char*
> (t)))});
422 virtual xmlNode* GetInternalTRep ()
override
426 static xmlNsPtr genNS2Use_ (xmlNode* n,
const URI& ns)
428 xmlNsPtr ns2Use = xmlSearchNsByHref (n->doc, n, BAD_CAST ns.
As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str ());
429 string prefix2Try{
"a"};
430 while (ns2Use ==
nullptr) {
436 ns2Use = xmlNewNs (n, BAD_CAST ns.
As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str (), BAD_CAST prefix2Try.c_str ());
437 if (ns2Use ==
nullptr) {
450 struct ElementRep_ : Element::
IRep, Memory::InheritAndUseBlockAllocationIfAppropriate<ElementRep_, NodeRep_> {
452 ElementRep_ (xmlNode* n)
456 Require (n->type == XML_ELEMENT_NODE);
458 virtual Node::Type GetNodeType ()
const override
461 Assert (fNode_->type == XML_ELEMENT_NODE);
462 return Node::eElementNT;
464 virtual optional<String> GetAttribute (
const NameWithNamespace& attrName)
const override
466 auto r = attrName.fNamespace ? xmlGetNsProp (fNode_, BAD_CAST attrName.fName.
AsUTF8 ().c_str (),
467 BAD_CAST attrName.fNamespace->As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str ())
468 : xmlGetProp (fNode_, BAD_CAST attrName.fName.AsUTF8 ().c_str ());
472 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([&] ()
noexcept { xmlFree (r); });
475 virtual void SetAttribute (
const NameWithNamespace& attrName,
const optional<String>& v)
override
478 Require (GetNodeType () == Node::eElementNT);
479 if (attrName == kXMLNS) {
486 xmlSetNsProp (fNode_, GetSharedReUsableXMLNSParentNamespace_ (fNode_), BAD_CAST attrName.fName.
AsUTF8 ().c_str (),
487 v == nullopt ?
nullptr : (BAD_CAST v->AsUTF8 ().c_str ()));
489 else if (attrName.fNamespace) {
491 xmlSetNsProp (fNode_, genNS2Use_ (fNode_, *attrName.fNamespace), BAD_CAST attrName.fName.
AsUTF8 ().c_str (),
492 v == nullopt ?
nullptr : (BAD_CAST v->AsUTF8 ().c_str ()));
495 xmlSetProp (fNode_, BAD_CAST attrName.fName.
AsUTF8 ().c_str (), v == nullopt ?
nullptr : (BAD_CAST v->AsUTF8 ().c_str ()));
500#if qStroika_Foundation_Debug_AssertionsChecked
501 Require (ValidNewNodeName_ (eltName.fName));
503 Require (afterNode ==
nullptr or this->GetChildren ().Contains (afterNode));
505 xmlNs* useNS = eltName.fNamespace ? genNS2Use_ (fNode_, *eltName.fNamespace) : fNode_->ns;
506 xmlNode* newNode = xmlNewNode (useNS, BAD_CAST eltName.fName.
AsUTF8 ().c_str ());
507 NodeRep_* afterNodeRep = afterNode ==
nullptr ? nullptr :
dynamic_cast<NodeRep_*
> (afterNode.
GetRep ().get ());
508 if (afterNodeRep ==
nullptr) {
511 if (fNode_->children ==
nullptr) {
512 xmlAddChild (fNode_->parent, newNode);
515 xmlAddPrevSibling (fNode_->children, newNode);
519 xmlAddNextSibling (afterNodeRep->fNode_, newNode);
521 return WrapLibXML2NodeInStroikaNode_ (newNode);
525#if qStroika_Foundation_Debug_AssertionsChecked
526 Require (ValidNewNodeName_ (eltName.fName));
529 xmlNs* useNS = eltName.fNamespace ? genNS2Use_ (fNode_, *eltName.fNamespace) : fNode_->ns;
530 xmlNode* newNode = xmlNewNode (useNS, BAD_CAST eltName.fName.
AsUTF8 ().c_str ());
531 xmlAddChild (fNode_, newNode);
532 return WrapLibXML2NodeInStroikaNode_ (newNode);
539 return Traversal::CreateGenerator<Node::Ptr> ([curChild = fNode_->children] () mutable -> optional<Node::Ptr> {
540 if (curChild == nullptr) {
541 return optional<Node::Ptr>{};
543 Node::Ptr r = WrapLibXML2NodeInStroikaNode_ (curChild);
544 curChild = curChild->next;
548 struct XPathLookupHelper_ {
549 xmlXPathContext* fCtx{
nullptr};
550 xmlXPathObject* fResultNodeList{
nullptr};
551 XPathLookupHelper_ () =
delete;
552 XPathLookupHelper_ (
const XPathLookupHelper_&) =
delete;
553 XPathLookupHelper_ (xmlDoc* doc, xmlNode* contextNode,
const XPath::Expression& e)
557 fCtx = xmlXPathNewContext (doc);
559 fCtx->error = [] ([[maybe_unused]]
void* userData, [[maybe_unused]]
const xmlError* error) {
563 auto namespaceDefs = e.
GetOptions ().fNamespaces;
564 if (namespaceDefs.GetDefaultNamespace ()) {
569 xmlXPathRegisterNs (fCtx, BAD_CAST ni.fKey.AsUTF8 ().c_str (),
570 BAD_CAST ni.fValue.As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str ());
572 fCtx->node = contextNode;
573 fResultNodeList = xmlXPathEvalExpression (BAD_CAST e.
GetExpression ().
AsUTF8 ().c_str (), fCtx);
574 if (fCtx->lastError.level != XML_ERR_NONE and fCtx->lastError.level != XML_ERR_WARNING) {
577 e, fCtx->lastError.domain, fCtx->lastError.code)});
582 if (fResultNodeList !=
nullptr) {
583 xmlXPathFreeObject (fResultNodeList);
584 fResultNodeList =
nullptr;
586 if (fCtx !=
nullptr) {
587 xmlXPathFreeContext (fCtx);
593 ~XPathLookupHelper_ ()
596 xmlXPathFreeObject (fResultNodeList);
598 xmlXPathFreeContext (fCtx);
600 static optional<XPath::Result> ToResult (xmlNode* n)
602 if (n ==
nullptr) [[unlikely]] {
607 case XML_ATTRIBUTE_NODE:
608 case XML_ELEMENT_NODE: {
609 return WrapLibXML2NodeInStroikaNode_ (n);
619 XPathLookupHelper_ helper{fNode_->doc, fNode_, e};
620 xmlNodeSet* resultSet = helper.fResultNodeList->nodesetval;
621 size_t size = (resultSet) ? resultSet->nodeNr : 0;
623 return XPathLookupHelper_::ToResult (resultSet->nodeTab[0]);
636 XPathLookupHelper_ helper{fNode_->doc, fNode_, e};
637 xmlNodeSet* resultSet = helper.fResultNodeList->nodesetval;
638 size_t size = (resultSet) ? resultSet->nodeNr : 0;
640 for (
size_t i = 0; i < size; ++i) {
641 r += Memory::ValueOf (XPathLookupHelper_::ToResult (resultSet->nodeTab[i]));
646 DISABLE_COMPILER_MSC_WARNING_END (4250)
650 Node::Ptr WrapLibXML2NodeInStroikaNode_ (xmlNode* n)
653 if (n->type == XML_ELEMENT_NODE) [[likely]] {
654 return Node::Ptr{Memory::MakeSharedPtr<ElementRep_> (n)};
657 return Node::Ptr{Memory::MakeSharedPtr<NodeRep_> (n)};
663 struct MyLibXML2StructuredErrGrabber_ {
664 xmlParserCtxtPtr fCtx;
665 shared_ptr<Execution::RuntimeErrorException<>> fCapturedException;
667 MyLibXML2StructuredErrGrabber_ (xmlParserCtxtPtr ctx)
670 xmlCtxtSetErrorHandler (ctx, xmlStructuredErrorFunc_,
this);
672 ~MyLibXML2StructuredErrGrabber_ ()
674 xmlCtxtSetErrorHandler (fCtx,
nullptr,
nullptr);
676 MyLibXML2StructuredErrGrabber_& operator= (
const MyLibXML2StructuredErrGrabber_&) =
delete;
680 if (fCapturedException !=
nullptr) {
686 static void xmlStructuredErrorFunc_ (
void* userData,
const xmlError* error)
689 MyLibXML2StructuredErrGrabber_* useThis =
reinterpret_cast<MyLibXML2StructuredErrGrabber_*
> (userData);
691 if (useThis->fCapturedException ==
nullptr) {
692 switch (error->level) {
696 case XML_ERR_WARNING:
697 DbgTrace (
"libxml2 (xmlStructuredErrorFunc_): Ignore warnings for now: {}"_f, String::FromUTF8 (error->message));
701 DbgTrace (
"libxml2 (xmlStructuredErrorFunc_): Capturing Error {}"_f, String::FromUTF8 (error->message));
702 useThis->fCapturedException = make_shared<DataExchange::BadFormatException> (
703 "Failure Parsing XML: {}, line {}"_f(String::FromUTF8 (error->message), error->line),
704 static_cast<unsigned int> (error->line), nullopt, nullopt);
713 struct DocRep_ : ILibXML2DocRep {
714#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
715 static inline atomic<unsigned int> sLiveCnt{0};
721 fLibRep_ = xmlNewDoc (BAD_CAST
"1.0");
722 fLibRep_->standalone =
true;
725 xmlParserCtxtPtr ctxt = xmlCreatePushParserCtxt (
nullptr,
nullptr,
nullptr, 0,
"in-stream.xml" );
727 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([&] ()
noexcept { xmlFreeParserCtxt (ctxt); });
728 MyLibXML2StructuredErrGrabber_ errCatcher{ctxt};
731 if (xmlParseChunk (ctxt,
reinterpret_cast<char*
> (buf),
static_cast<int> (n), 0)) {
734 errCatcher.ThrowIf ();
736 xmlParseChunk (ctxt,
nullptr, 0, 1);
737 errCatcher.ThrowIf ();
738 if (not ctxt->wellFormed) {
741 fLibRep_ = ctxt->myDoc;
748 fLibRep_->_private =
this;
749#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
753 DocRep_ (
const DocRep_& from)
755 fLibRep_ = xmlCopyDoc (from.fLibRep_, 1);
756 fLibRep_->_private =
this;
757#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
761 DocRep_ (DocRep_&&) =
delete;
762 DocRep_& operator= (DocRep_&) =
delete;
766 Assert (fLibRep_->_private ==
this);
767 xmlFreeDoc (fLibRep_);
768 for (
auto i : fNSs2Free_) {
771#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
772 Assert (sLiveCnt > 0);
776 virtual xmlDoc* GetLibXMLDocRep ()
override
780 virtual const Providers::IDOMProvider* GetProvider ()
const override
782 return &Providers::LibXML2::kDefaultProvider;
784 virtual bool GetStandalone ()
const override
786 return !!fLibRep_->standalone;
788 virtual void SetStandalone (
bool standalone)
override
790 fLibRep_->standalone = standalone;
798 xmlNsPtr ns{
nullptr};
799 if (newEltName.fNamespace) {
800 if (childrenInheritNS) {
801 ns = xmlNewNs (
nullptr, BAD_CAST newEltName.fNamespace->As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str (),
nullptr);
805 ns = xmlNewNs (
nullptr, BAD_CAST newEltName.fNamespace->As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str (), BAD_CAST
"x");
808 xmlNodePtr n = xmlNewDocNode (fLibRep_, ns, BAD_CAST newEltName.fName.
AsUTF8 ().c_str (),
nullptr);
809 Assert (n->nsDef ==
nullptr);
810 if (childrenInheritNS and newEltName.fNamespace) {
813 xmlDocSetRootElement (fLibRep_, n);
814 auto r = WrapLibXML2NodeInStroikaNode_ (n);
815 Ensure (r.GetName () == newEltName);
822 xmlBufferPtr xmlBuf = xmlBufferCreate ();
823 [[maybe_unused]]
auto&& cleanup1 =
Execution::Finally ([&] ()
noexcept { xmlBufferFree (xmlBuf); });
824 constexpr char kTxtEncoding_[] =
"UTF-8";
825 int useOptions = XML_SAVE_AS_XML;
826 if (options.fPrettyPrint) {
827 useOptions |= XML_SAVE_FORMAT | XML_SAVE_WSNONSIG;
830 xmlSaveCtxtPtr saveCtx = xmlSaveToBuffer (xmlBuf, kTxtEncoding_, useOptions);
831 [[maybe_unused]]
auto&& cleanup2 =
Execution::Finally ([&] ()
noexcept { xmlSaveClose (saveCtx); });
833 if (xmlSaveDoc (saveCtx, fLibRep_) >= 0 and xmlSaveFlush (saveCtx) >= 0) {
834 to.
Write (span{
reinterpret_cast<const byte*
> (xmlBufferContent (xmlBuf)),
static_cast<size_t> (xmlBufferLength (xmlBuf))});
844 return Traversal::CreateGenerator<Node::Ptr> ([curChild = fLibRep_->children] () mutable -> optional<Node::Ptr> {
845 if (curChild == nullptr) {
846 return optional<Node::Ptr>{};
848 Node::Ptr r = WrapLibXML2NodeInStroikaNode_ (curChild);
849 curChild = curChild->next;
853 virtual void Validate (
const Schema::Ptr& schema)
const override
857 Require (schema.GetRep ()->GetProvider () == &XML::Providers::LibXML2::kDefaultProvider);
858 xmlSchema* libxml2Schema = dynamic_pointer_cast<ILibXML2SchemaRep> (schema.GetRep ())->GetSchemaLibRep ();
860 xmlSchemaValidCtxtPtr validateCtx = xmlSchemaNewValidCtxt (libxml2Schema);
861 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([&] ()
noexcept { xmlSchemaFreeValidCtxt (validateCtx); });
863 static void warnFun ([[maybe_unused]]
void* ctx, [[maybe_unused]]
const char* msg, ...)
866 DbgTrace (
"validate warn function ignored"_f);
868 static void errFun (
void* ctx,
const char* msg, ...)
871 auto useCtx =
reinterpret_cast<ValCB_*
> (ctx);
872 if (useCtx->msg.empty ()) {
874 va_start (argsList, msg);
875 auto b = Characters::CString::FormatV (msg, argsList);
883 xmlSchemaSetValidErrors (validateCtx, &ValCB_::errFun, &ValCB_::warnFun, &validationCB);
884 int r = xmlSchemaValidateDoc (validateCtx, fLibRep_);
886 Assert (not validationCB.msg.empty ());
887 optional<unsigned int> lineNumber;
888 optional<unsigned int> columnNumber;
889 optional<uint64_t> fileOffset;
890 if (
const xmlError* err = xmlGetLastError ()) {
891 lineNumber =
static_cast<unsigned int> (err->line);
892 if (
static_cast<unsigned int> (err->int2) != 0) {
893 columnNumber =
static_cast<unsigned int> (err->int2);
899 xmlDoc* fLibRep_{
nullptr};
900 xmlNs* fXmlnsNamespace2Use{
nullptr};
901 list<xmlNsPtr> fNSs2Free_;
904 DocRep_* GetWrapperDoc_ (xmlDoc* d)
907 DocRep_* wrapperDoc =
reinterpret_cast<DocRep_*
> (d->_private);
908 Assert (wrapperDoc->fLibRep_ == d);
911 xmlNs* GetSharedReUsableXMLNSParentNamespace_ (xmlDoc* d)
913 auto wrapperDoc = GetWrapperDoc_ (d);
914 if (wrapperDoc->fXmlnsNamespace2Use ==
nullptr) {
916 wrapperDoc->fXmlnsNamespace2Use =
917 xmlNewNs (
nullptr, BAD_CAST kXMLNS.fNamespace->As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str (),
nullptr);
918 wrapperDoc->fNSs2Free_.push_front (wrapperDoc->fXmlnsNamespace2Use);
920 return wrapperDoc->fXmlnsNamespace2Use;
929String Providers::LibXML2::libXMLString2String (
const xmlChar* s,
int len)
931 return String{span{
reinterpret_cast<const char*
> (s),
static_cast<size_t> (len)}};
933String Providers::LibXML2::libXMLString2String (
const xmlChar* t)
935 return String::FromUTF8 (
reinterpret_cast<const char*
> (t));
943Providers::LibXML2::Provider::Provider ()
946#if qStroika_Foundation_Debug_AssertionsChecked
947 static unsigned int sNProvidersCreated_{0};
948 Assert (++sNProvidersCreated_ == 1);
953Providers::LibXML2::Provider::~Provider ()
956#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
957 Require (SchemaRep_::sLiveCnt == 0);
958 Require (DocRep_::sLiveCnt == 0);
965 return Memory::MakeSharedPtr<SchemaRep_> (schemaData, resolver);
969 const Schema::Ptr& schemaToValidateAgainstWhileReading)
const
971 auto r = Memory::MakeSharedPtr<DocRep_> (in);
972 if (schemaToValidateAgainstWhileReading !=
nullptr) {
973 r->Validate (schemaToValidateAgainstWhileReading);
982 optional<Streams::SeekOffsetType> seek2;
983 if (schema !=
nullptr and callback !=
nullptr) {
985 useInput = Streams::ToSeekableInputStream::New (in);
988 if (schema !=
nullptr) {
989 DocRep_ dr{useInput};
990 dr.Validate (schema);
992 if (callback !=
nullptr) {
993 SAXReader_ handler{*callback};
994 xmlParserCtxtPtr ctxt = xmlCreatePushParserCtxt (&handler.flibXMLSaxHndler_, &handler,
nullptr, 0,
nullptr);
996 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([&] ()
noexcept { xmlFreeParserCtxt (ctxt); });
997 MyLibXML2StructuredErrGrabber_ errCatcher{ctxt};
1000 useInput.
Seek (*seek2);
1002 while (
auto n = useInput.
ReadBlocking (span{buf}).size ()) {
1003 if (xmlParseChunk (ctxt,
reinterpret_cast<char*
> (buf),
static_cast<int> (n), 0)) {
1006 errCatcher.ThrowIf ();
1008 xmlParseChunk (ctxt,
nullptr, 0, 1);
1009 errCatcher.ThrowIf ();
#define RequireNotNull(p)
#define AssertNotReached()
conditional_t< qStroika_Foundation_Memory_PreferBlockAllocation and andTrueCheck, BlockAllocationUseHelper< T >, Common::Empty > UseBlockAllocationIfAppropriate
Use this to enable block allocation for a particular class. Beware of subclassing.
bool Equals(const T *lhs, const T *rhs)
strcmp or wsccmp() as appropriate == 0
#define CompileTimeFlagChecker_SOURCE(NS_PREFIX, NAME, VALUE)
String libXMLString2String(const xmlChar *s, int len)
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual T AsUTF8() const
nonvirtual String Trim(bool(*shouldBeTrimmed)(Character)=Character::IsWhitespace) const
nonvirtual size_t find(Character c, size_t startAt=0) const
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual shared_ptr< IRep > GetRep() const
return the associated shared_ptr (cannot be nullptr)
Node::Ptr is a smart pointer to a Node::IRep.
nonvirtual String GetExpression() const
nonvirtual Options GetOptions() const
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
shared_lock< const AssertExternallySynchronizedMutex > ReadContext
Instantiate AssertExternallySynchronizedMutex::ReadContext to designate an area of code where protect...
nonvirtual T As(optional< StringPCTEncodedFlag > pctEncoded={}) const
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
nonvirtual void Write(span< ELEMENT_TYPE2, EXTENT_2 > elts) const
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
wstring NarrowSDK2Wide(span< const char > s)
DISABLE_COMPILER_MSC_WARNING_START(4996)
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
void ThrowIfNull(const Private_::ConstVoidStar &p, const HRESULT &hr)
Template specialization for ThrowIfNull (), for thing being thrown HRESULT - really throw HRESULTErro...
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >