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;
36using Memory::MakeSharedPtr;
41static_assert (qStroika_HasComponent_libxml2,
"Don't compile this file if qStroika_HasComponent_libxml2 not set");
52 constexpr auto kUseURIEncodingFlag_ = URI::StringPCTEncodedFlag::eDecoded;
61 struct RegisterResolver_ final {
62 static inline thread_local RegisterResolver_* sCurrent_ =
nullptr;
66 : fResolver_{resolver}
69 if (resolver !=
nullptr) {
71 if (xmlRegisterInputCallbacks (ResolverMatch_, ResolverOpen_, ResolverRead_, ResolverClose_) < 0) {
87 static int ResolverMatch_ (
const char*
URI)
91 optional<Resource::Definition> r = sCurrent_->fResolver_.Lookup (
Resource::Name{
92 .fNamespace = String::FromUTF8 (
URI), .fPublicID = String::FromUTF8 (
URI), .fSystemID = String::FromUTF8 (
URI)});
94 DbgTrace (
"Note ResolveMatch {} failed to find an entry in resolver."_f, String::FromUTF8 (
URI));
96 return r.has_value ();
106 static void* ResolverOpen_ (
const char*
URI)
109 optional<Resource::Definition> r = sCurrent_->fResolver_.Lookup (
Resource::Name{
110 .fNamespace = String::FromUTF8 (
URI), .fPublicID = String::FromUTF8 (
URI), .fSystemID = String::FromUTF8 (
URI)});
113 if (r.has_value ()) {
127 static int ResolverClose_ (
void* context)
129 if (context ==
nullptr)
143 static int ResolverRead_ (
void* context,
char* buffer,
int len)
147 span<byte> r = inStream->
ReadBlocking (as_writable_bytes (span{buffer,
static_cast<size_t> (len)}));
148 return static_cast<int> (r.size ());
155 struct SchemaRep_ final : ILibXML2SchemaRep {
156#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
157 static inline atomic<unsigned int> sLiveCnt{0};
160 : fResolver_{resolver}
161 , fSchemaData{schemaData.ReadAll ()}
163 RegisterResolver_ registerResolver{resolver};
164 xmlSchemaParserCtxt* schemaParseContext =
165 xmlSchemaNewMemParserCtxt (
reinterpret_cast<const char*
> (fSchemaData.data ()),
static_cast<int> (fSchemaData.size ()));
166 fCompiledSchema = xmlSchemaParse (schemaParseContext);
167 xmlSchemaFreeParserCtxt (schemaParseContext);
168 Execution::ThrowIfNull (fCompiledSchema);
169#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
173 SchemaRep_ (
const SchemaRep_&) =
delete;
174 virtual ~SchemaRep_ ()
176 xmlSchemaFree (fCompiledSchema);
177#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
178 Assert (sLiveCnt > 0);
184 xmlSchema* fCompiledSchema{
nullptr};
186 virtual const Providers::ISchemaProvider* GetProvider ()
const override
188 return &XML::Providers::LibXML2::kDefaultProvider;
190 virtual optional<URI> GetTargetNamespace ()
const override
192 Assert (fCompiledSchema !=
nullptr);
193 if (fCompiledSchema->targetNamespace !=
nullptr) {
207 virtual xmlSchema* GetSchemaLibRep ()
override
210 return fCompiledSchema;
216 struct SAXReader_ final {
218 xmlSAXHandler flibXMLSaxHndler_{};
221 : fCallback_{callback}
223 flibXMLSaxHndler_.initialized = XML_SAX2_MAGIC;
224 flibXMLSaxHndler_.startDocument = [] (
void* ctx) {
225 SAXReader_* thisReader =
reinterpret_cast<SAXReader_*
> (ctx);
226 Assert (thisReader->flibXMLSaxHndler_.initialized == XML_SAX2_MAGIC);
227 thisReader->fCallback_.StartDocument ();
229 flibXMLSaxHndler_.endDocument = [] (
void* ctx) {
230 SAXReader_* thisReader =
reinterpret_cast<SAXReader_*
> (ctx);
231 Assert (thisReader->flibXMLSaxHndler_.initialized == XML_SAX2_MAGIC);
232 thisReader->fCallback_.EndDocument ();
234 flibXMLSaxHndler_.startElementNs = [] (
void* ctx,
const xmlChar* localname, [[maybe_unused]]
const xmlChar* prefix,
const xmlChar*
URI,
235 [[maybe_unused]]
int nb_namespaces, [[maybe_unused]]
const xmlChar** namespaces,
236 int nb_attributes, [[maybe_unused]]
int nb_defaulted,
const xmlChar** attributes) {
237 SAXReader_* thisReader =
reinterpret_cast<SAXReader_*
> (ctx);
238 Assert (thisReader->flibXMLSaxHndler_.initialized == XML_SAX2_MAGIC);
240 if (attributes !=
nullptr) {
243 auto ai = attributes;
244 for (
int i = 0; i < nb_attributes; ++i) {
251 if (
URI ==
nullptr) {
258 flibXMLSaxHndler_.endElementNs = [] (
void* ctx,
const xmlChar* localname, [[maybe_unused]]
const xmlChar* prefix,
const xmlChar*
URI) {
259 SAXReader_* thisReader =
reinterpret_cast<SAXReader_*
> (ctx);
260 if (
URI ==
nullptr) {
268 flibXMLSaxHndler_.characters = [] (
void* ctx,
const xmlChar* ch,
int len) {
269 SAXReader_* thisReader =
reinterpret_cast<SAXReader_*
> (ctx);
271 thisReader->fCallback_.TextInsideElement (String::FromUTF8 (span{
reinterpret_cast<const char*
> (ch),
static_cast<size_t> (len)}));
274 SAXReader_ (
const SAXReader_&) =
delete;
279#if qStroika_Foundation_Debug_AssertionsChecked
280 bool ValidNewNodeName_ (
const String& n)
285 if (n.
find (
':') != wstring::npos) {
295 DocRep_* GetWrapperDoc_ (xmlDoc* d);
296 DocRep_* GetWrapperDoc_ (xmlNode* n)
300 return GetWrapperDoc_ (n->doc);
302 xmlNs* GetSharedReUsableXMLNSParentNamespace_ (xmlDoc* d);
303 xmlNs* GetSharedReUsableXMLNSParentNamespace_ (xmlNode* n)
307 return GetSharedReUsableXMLNSParentNamespace_ (n->doc);
312 Node::Ptr WrapLibXML2NodeInStroikaNode_ (xmlNode* n);
318 NodeRep_ (xmlNode* n)
323 virtual const Providers::IDOMProvider* GetProvider ()
const override
325 return &Providers::LibXML2::kDefaultProvider;
327 virtual bool Equals (
const IRep* rhs)
const override
331 return fNode_ ==
dynamic_cast<const NodeRep_*
> (rhs)->fNode_;
333 virtual Node::Type GetNodeType ()
const override
336 switch (fNode_->type) {
337 case XML_ELEMENT_NODE:
338 return Node::eElementNT;
339 case XML_ATTRIBUTE_NODE:
340 return Node::eAttributeNT;
342 return Node::eTextNT;
343 case XML_COMMENT_NODE:
344 return Node::eCommentNT;
346 return Node::eOtherNT;
352 Require (GetNodeType () == Node::eElementNT or GetNodeType () == Node::eAttributeNT);
353 switch (fNode_->type) {
354 case XML_ATTRIBUTE_NODE:
355 case XML_ELEMENT_NODE: {
356 const xmlChar* ns = fNode_->ns ==
nullptr ? nullptr : fNode_->ns->href;
367#if qStroika_Foundation_Debug_AssertionsChecked
368 Require (ValidNewNodeName_ (name.fName));
370 if (name.fNamespace) {
375 xmlNodeSetName (fNode_, BAD_CAST name.fName.
AsUTF8 ().c_str ());
377 virtual String GetValue ()
const override
380 auto r = xmlNodeGetContent (fNode_);
381 [[maybe_unused]]
auto&& cleanup = Execution::Finally ([&] ()
noexcept { xmlFree (r); });
384 virtual void SetValue (
const String& v)
override
388 bool mustEncode =
true;
390 xmlChar* p = xmlEncodeSpecialChars (fNode_->doc, BAD_CAST v.
AsUTF8 ().c_str ());
391 [[maybe_unused]]
auto&& cleanup = Execution::Finally ([&] ()
noexcept { xmlFree (p); });
392 ::xmlNodeSetContent (fNode_, p);
395 ::xmlNodeSetContent (fNode_, BAD_CAST v.
AsUTF8 ().c_str ());
398 virtual void DeleteNode ()
override
401 ::xmlUnlinkNode (fNode_);
402 ::xmlFreeNode (fNode_);
405 virtual Node::Ptr GetParentNode ()
const override
408 if (fNode_->parent ==
nullptr) {
411 return WrapLibXML2NodeInStroikaNode_ (fNode_->parent);
415 xmlBufferPtr xmlBuf = xmlBufferCreate ();
416 [[maybe_unused]]
auto&& cleanup = Execution::Finally ([&] ()
noexcept { ::xmlBufferFree (xmlBuf); });
417 if (
int dumpRes = ::xmlNodeDump (xmlBuf, fNode_->doc, fNode_, 0, options.fPrettyPrint); dumpRes == -1) {
420 const xmlChar* t = xmlBufferContent (xmlBuf);
422 to.
Write (span{
reinterpret_cast<const byte*
> (t),
static_cast<size_t> (::strlen (
reinterpret_cast<const char*
> (t)))});
424 virtual xmlNode* GetInternalTRep ()
override
428 static xmlNsPtr genNS2Use_ (xmlNode* n,
const URI& ns)
430 xmlNsPtr ns2Use = xmlSearchNsByHref (n->doc, n, BAD_CAST ns.
As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str ());
431 string prefix2Try{
"a"};
432 while (ns2Use ==
nullptr) {
438 ns2Use = xmlNewNs (n, BAD_CAST ns.
As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str (), BAD_CAST prefix2Try.c_str ());
439 if (ns2Use ==
nullptr) {
452 struct ElementRep_ : Element::
IRep, Memory::InheritAndUseBlockAllocationIfAppropriate<ElementRep_, NodeRep_> {
454 ElementRep_ (xmlNode* n)
458 Require (n->type == XML_ELEMENT_NODE);
460 virtual Node::Type GetNodeType ()
const override
463 Assert (fNode_->type == XML_ELEMENT_NODE);
464 return Node::eElementNT;
466 virtual optional<String> GetAttribute (
const NameWithNamespace& attrName)
const override
468 auto r = attrName.fNamespace ? ::xmlGetNsProp (fNode_, BAD_CAST attrName.fName.
AsUTF8 ().c_str (),
469 BAD_CAST attrName.fNamespace->As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str ())
470 : ::xmlGetProp (fNode_, BAD_CAST attrName.fName.AsUTF8 ().c_str ());
474 [[maybe_unused]]
auto&& cleanup = Execution::Finally ([&] ()
noexcept { xmlFree (r); });
475 return ::libXMLString2String (r);
477 virtual void SetAttribute (
const NameWithNamespace& attrName,
const optional<String>& v)
override
480 Require (GetNodeType () == Node::eElementNT);
481 if (attrName == kXMLNS) {
488 ::xmlSetNsProp (fNode_, GetSharedReUsableXMLNSParentNamespace_ (fNode_), BAD_CAST attrName.fName.
AsUTF8 ().c_str (),
489 v == nullopt ?
nullptr : (BAD_CAST v->AsUTF8 ().c_str ()));
491 else if (attrName.fNamespace) {
493 ::xmlSetNsProp (fNode_, genNS2Use_ (fNode_, *attrName.fNamespace), BAD_CAST attrName.fName.
AsUTF8 ().c_str (),
494 v == nullopt ?
nullptr : (BAD_CAST v->AsUTF8 ().c_str ()));
497 ::xmlSetProp (fNode_, BAD_CAST attrName.fName.
AsUTF8 ().c_str (), v == nullopt ?
nullptr : (BAD_CAST v->AsUTF8 ().c_str ()));
502#if qStroika_Foundation_Debug_AssertionsChecked
503 Require (ValidNewNodeName_ (eltName.fName));
505 Require (afterNode ==
nullptr or this->GetChildren ().Contains (afterNode));
507 xmlNs* useNS = eltName.fNamespace ? genNS2Use_ (fNode_, *eltName.fNamespace) : fNode_->ns;
508 xmlNode* newNode = ::xmlNewNode (useNS, BAD_CAST eltName.fName.
AsUTF8 ().c_str ());
509 NodeRep_* afterNodeRep = afterNode ==
nullptr ? nullptr :
dynamic_cast<NodeRep_*
> (afterNode.
GetRep ().get ());
510 if (afterNodeRep ==
nullptr) {
513 if (fNode_->children ==
nullptr) {
514 ::xmlAddChild (fNode_->parent, newNode);
517 ::xmlAddPrevSibling (fNode_->children, newNode);
521 xmlAddNextSibling (afterNodeRep->fNode_, newNode);
523 return WrapLibXML2NodeInStroikaNode_ (newNode);
527#if qStroika_Foundation_Debug_AssertionsChecked
528 Require (ValidNewNodeName_ (eltName.fName));
531 xmlNs* useNS = eltName.fNamespace ? genNS2Use_ (fNode_, *eltName.fNamespace) : fNode_->ns;
532 xmlNode* newNode = xmlNewNode (useNS, BAD_CAST eltName.fName.
AsUTF8 ().c_str ());
533 ::xmlAddChild (fNode_, newNode);
534 return WrapLibXML2NodeInStroikaNode_ (newNode);
541 return Traversal::CreateGenerator<Node::Ptr> ([curChild = fNode_->children] () mutable -> optional<Node::Ptr> {
542 if (curChild == nullptr) {
543 return optional<Node::Ptr>{};
545 Node::Ptr r = WrapLibXML2NodeInStroikaNode_ (curChild);
546 curChild = curChild->next;
550 struct XPathLookupHelper_ final {
551 xmlXPathContext* fCtx{
nullptr};
552 xmlXPathObject* fResultNodeList{
nullptr};
553 XPathLookupHelper_ () =
delete;
554 XPathLookupHelper_ (
const XPathLookupHelper_&) =
delete;
555 XPathLookupHelper_ (xmlDoc* doc, xmlNode* contextNode,
const XPath::Expression& e)
559 fCtx = xmlXPathNewContext (doc);
560 Execution::ThrowIfNull (fCtx);
561 fCtx->error = [] ([[maybe_unused]]
void* userData, [[maybe_unused]]
const xmlError* error) {
565 auto namespaceDefs = e.
GetOptions ().fNamespaces;
566 if (namespaceDefs.GetDefaultNamespace ()) {
568 Execution::Throw (XPath::XPathExpressionNotSupported::kThe);
571 xmlXPathRegisterNs (fCtx, BAD_CAST ni.fKey.AsUTF8 ().c_str (),
572 BAD_CAST ni.fValue.As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str ());
574 fCtx->node = contextNode;
575 fResultNodeList = xmlXPathEvalExpression (BAD_CAST e.
GetExpression ().
AsUTF8 ().c_str (), fCtx);
576 if (fCtx->lastError.level != XML_ERR_NONE and fCtx->lastError.level != XML_ERR_WARNING) {
579 e, fCtx->lastError.domain, fCtx->lastError.code)});
581 Execution::ThrowIfNull (fResultNodeList);
584 if (fResultNodeList !=
nullptr) {
585 xmlXPathFreeObject (fResultNodeList);
586 fResultNodeList =
nullptr;
588 if (fCtx !=
nullptr) {
589 xmlXPathFreeContext (fCtx);
592 Execution::ReThrow ();
595 ~XPathLookupHelper_ ()
598 xmlXPathFreeObject (fResultNodeList);
600 xmlXPathFreeContext (fCtx);
602 static optional<XPath::Result> ToResult (xmlNode* n)
604 if (n ==
nullptr) [[unlikely]] {
609 case XML_ATTRIBUTE_NODE:
610 case XML_ELEMENT_NODE: {
611 return WrapLibXML2NodeInStroikaNode_ (n);
621 XPathLookupHelper_ helper{fNode_->doc, fNode_, e};
622 xmlNodeSet* resultSet = helper.fResultNodeList->nodesetval;
623 size_t size = (resultSet) ? resultSet->nodeNr : 0;
625 return XPathLookupHelper_::ToResult (resultSet->nodeTab[0]);
638 XPathLookupHelper_ helper{fNode_->doc, fNode_, e};
639 xmlNodeSet* resultSet = helper.fResultNodeList->nodesetval;
640 size_t size = (resultSet) ? resultSet->nodeNr : 0;
642 for (
size_t i = 0; i < size; ++i) {
643 r += Memory::ValueOf (XPathLookupHelper_::ToResult (resultSet->nodeTab[i]));
648 DISABLE_COMPILER_MSC_WARNING_END (4250)
652 Node::Ptr WrapLibXML2NodeInStroikaNode_ (xmlNode* n)
655 if (n->type == XML_ELEMENT_NODE) [[likely]] {
656 return Node::Ptr{MakeSharedPtr<ElementRep_> (n)};
659 return Node::Ptr{MakeSharedPtr<NodeRep_> (n)};
665 struct MyLibXML2StructuredErrGrabber_ final {
666 xmlParserCtxtPtr fCtx;
667 shared_ptr<Execution::RuntimeErrorException<>> fCapturedException;
669 MyLibXML2StructuredErrGrabber_ (xmlParserCtxtPtr ctx)
672 xmlCtxtSetErrorHandler (ctx, xmlStructuredErrorFunc_,
this);
674 ~MyLibXML2StructuredErrGrabber_ ()
676 xmlCtxtSetErrorHandler (fCtx,
nullptr,
nullptr);
678 MyLibXML2StructuredErrGrabber_& operator= (
const MyLibXML2StructuredErrGrabber_&) =
delete;
682 if (fCapturedException !=
nullptr) {
683 Execution::Throw (*fCapturedException);
688 static void xmlStructuredErrorFunc_ (
void* userData,
const xmlError* error)
691 MyLibXML2StructuredErrGrabber_* useThis =
reinterpret_cast<MyLibXML2StructuredErrGrabber_*
> (userData);
693 if (useThis->fCapturedException ==
nullptr) {
694 switch (error->level) {
698 case XML_ERR_WARNING:
699 DbgTrace (
"libxml2 (xmlStructuredErrorFunc_): Ignore warnings for now: {}"_f, String::FromUTF8 (error->message));
703 DbgTrace (
"libxml2 (xmlStructuredErrorFunc_): Capturing Error {}"_f, String::FromUTF8 (error->message));
704 useThis->fCapturedException = MakeSharedPtr<DataExchange::BadFormatException> (
705 "Failure Parsing XML: {}, line {}"_f(String::FromUTF8 (error->message), error->line),
706 static_cast<unsigned int> (error->line), nullopt, nullopt);
715 struct DocRep_ final : ILibXML2DocRep {
716#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
717 static inline atomic<unsigned int> sLiveCnt{0};
723 fLibRep_ = xmlNewDoc (BAD_CAST
"1.0");
724 fLibRep_->standalone =
true;
727 xmlParserCtxtPtr ctxt = xmlCreatePushParserCtxt (
nullptr,
nullptr,
nullptr, 0,
"in-stream.xml" );
728 Execution::ThrowIfNull (ctxt);
729 [[maybe_unused]]
auto&& cleanup = Execution::Finally ([&] ()
noexcept { xmlFreeParserCtxt (ctxt); });
730 MyLibXML2StructuredErrGrabber_ errCatcher{ctxt};
733 if (xmlParseChunk (ctxt,
reinterpret_cast<char*
> (buf),
static_cast<int> (n), 0)) {
736 errCatcher.ThrowIf ();
738 xmlParseChunk (ctxt,
nullptr, 0, 1);
739 errCatcher.ThrowIf ();
740 if (not ctxt->wellFormed) {
743 fLibRep_ = ctxt->myDoc;
750 fLibRep_->_private =
this;
751#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
755 DocRep_ (
const DocRep_& from)
757 fLibRep_ = xmlCopyDoc (from.fLibRep_, 1);
758 fLibRep_->_private =
this;
759#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
763 DocRep_ (DocRep_&&) =
delete;
764 DocRep_& operator= (DocRep_&) =
delete;
768 Assert (fLibRep_->_private ==
this);
769 xmlFreeDoc (fLibRep_);
770 for (
auto i : fNSs2Free_) {
773#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
774 Assert (sLiveCnt > 0);
778 virtual xmlDoc* GetLibXMLDocRep ()
override
782 virtual const Providers::IDOMProvider* GetProvider ()
const override
784 return &Providers::LibXML2::kDefaultProvider;
786 virtual bool GetStandalone ()
const override
788 return !!fLibRep_->standalone;
790 virtual void SetStandalone (
bool standalone)
override
792 fLibRep_->standalone = standalone;
800 xmlNsPtr ns{
nullptr};
801 if (newEltName.fNamespace) {
802 if (childrenInheritNS) {
803 ns = xmlNewNs (
nullptr, BAD_CAST newEltName.fNamespace->As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str (),
nullptr);
807 ns = xmlNewNs (
nullptr, BAD_CAST newEltName.fNamespace->As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str (), BAD_CAST
"x");
810 xmlNodePtr n = xmlNewDocNode (fLibRep_, ns, BAD_CAST newEltName.fName.
AsUTF8 ().c_str (),
nullptr);
811 Assert (n->nsDef ==
nullptr);
812 if (childrenInheritNS and newEltName.fNamespace) {
815 xmlDocSetRootElement (fLibRep_, n);
816 auto r = WrapLibXML2NodeInStroikaNode_ (n);
817 Ensure (r.GetName () == newEltName);
824 xmlBufferPtr xmlBuf = xmlBufferCreate ();
825 [[maybe_unused]]
auto&& cleanup1 = Execution::Finally ([&] ()
noexcept { xmlBufferFree (xmlBuf); });
826 constexpr char kTxtEncoding_[] =
"UTF-8";
827 int useOptions = XML_SAVE_AS_XML;
828 if (options.fPrettyPrint) {
829 useOptions |= XML_SAVE_FORMAT | XML_SAVE_WSNONSIG;
832 xmlSaveCtxtPtr saveCtx = xmlSaveToBuffer (xmlBuf, kTxtEncoding_, useOptions);
833 [[maybe_unused]]
auto&& cleanup2 = Execution::Finally ([&] ()
noexcept { xmlSaveClose (saveCtx); });
835 if (xmlSaveDoc (saveCtx, fLibRep_) >= 0 and xmlSaveFlush (saveCtx) >= 0) {
836 to.
Write (span{
reinterpret_cast<const byte*
> (xmlBufferContent (xmlBuf)),
static_cast<size_t> (xmlBufferLength (xmlBuf))});
846 return Traversal::CreateGenerator<Node::Ptr> ([curChild = fLibRep_->children] () mutable -> optional<Node::Ptr> {
847 if (curChild == nullptr) {
848 return optional<Node::Ptr>{};
850 Node::Ptr r = WrapLibXML2NodeInStroikaNode_ (curChild);
851 curChild = curChild->next;
855 virtual void Validate (
const Schema::Ptr& schema)
const override
859 Require (schema.GetRep ()->GetProvider () == &XML::Providers::LibXML2::kDefaultProvider);
860 xmlSchema* libxml2Schema = dynamic_pointer_cast<ILibXML2SchemaRep> (schema.GetRep ())->GetSchemaLibRep ();
862 xmlSchemaValidCtxtPtr validateCtx = xmlSchemaNewValidCtxt (libxml2Schema);
863 [[maybe_unused]]
auto&& cleanup = Execution::Finally ([&] ()
noexcept { xmlSchemaFreeValidCtxt (validateCtx); });
865 static void warnFun ([[maybe_unused]]
void* ctx, [[maybe_unused]]
const char* msg, ...)
868 DbgTrace (
"validate warn function ignored"_f);
870 static void errFun (
void* ctx,
const char* msg, ...)
873 auto useCtx =
reinterpret_cast<ValCB_*
> (ctx);
874 if (useCtx->msg.empty ()) {
876 va_start (argsList, msg);
877 auto b = Characters::CString::FormatV (msg, argsList);
885 xmlSchemaSetValidErrors (validateCtx, &ValCB_::errFun, &ValCB_::warnFun, &validationCB);
886 int r = xmlSchemaValidateDoc (validateCtx, fLibRep_);
888 Assert (not validationCB.msg.empty ());
889 optional<unsigned int> lineNumber;
890 optional<unsigned int> columnNumber;
891 optional<uint64_t> fileOffset;
892 if (
const xmlError* err = xmlGetLastError ()) {
893 lineNumber =
static_cast<unsigned int> (err->line);
894 if (
static_cast<unsigned int> (err->int2) != 0) {
895 columnNumber =
static_cast<unsigned int> (err->int2);
898 Execution::Throw (
BadFormatException{validationCB.msg, lineNumber, columnNumber, fileOffset});
901 xmlDoc* fLibRep_{
nullptr};
902 xmlNs* fXmlnsNamespace2Use{
nullptr};
903 list<xmlNsPtr> fNSs2Free_;
906 DocRep_* GetWrapperDoc_ (xmlDoc* d)
909 DocRep_* wrapperDoc =
reinterpret_cast<DocRep_*
> (d->_private);
910 Assert (wrapperDoc->fLibRep_ == d);
913 xmlNs* GetSharedReUsableXMLNSParentNamespace_ (xmlDoc* d)
915 auto wrapperDoc = GetWrapperDoc_ (d);
916 if (wrapperDoc->fXmlnsNamespace2Use ==
nullptr) {
918 wrapperDoc->fXmlnsNamespace2Use =
919 xmlNewNs (
nullptr, BAD_CAST kXMLNS.fNamespace->As<
String> (kUseURIEncodingFlag_).
AsUTF8 ().c_str (),
nullptr);
920 wrapperDoc->fNSs2Free_.push_front (wrapperDoc->fXmlnsNamespace2Use);
922 return wrapperDoc->fXmlnsNamespace2Use;
931String Providers::LibXML2::libXMLString2String (
const xmlChar* s,
int len)
933 return String{span{
reinterpret_cast<const char*
> (s),
static_cast<size_t> (len)}};
935String Providers::LibXML2::libXMLString2String (
const xmlChar* t)
937 return String::FromUTF8 (
reinterpret_cast<const char*
> (t));
945Providers::LibXML2::Provider::Provider ()
948#if qStroika_Foundation_Debug_AssertionsChecked
949 static unsigned int sNProvidersCreated_{0};
950 Assert (++sNProvidersCreated_ == 1);
955Providers::LibXML2::Provider::~Provider ()
958#if qStroika_Foundation_DataExchange_XML_DebugMemoryAllocations
959 Require (SchemaRep_::sLiveCnt == 0);
960 Require (DocRep_::sLiveCnt == 0);
967 return MakeSharedPtr<SchemaRep_> (schemaData, resolver);
971 const Schema::Ptr& schemaToValidateAgainstWhileReading)
const
973 auto r = MakeSharedPtr<DocRep_> (in);
974 if (schemaToValidateAgainstWhileReading !=
nullptr) {
975 r->Validate (schemaToValidateAgainstWhileReading);
984 optional<Streams::SeekOffsetType> seek2;
985 if (schema !=
nullptr and callback !=
nullptr) {
987 useInput = Streams::ToSeekableInputStream::New (in);
990 if (schema !=
nullptr) {
991 DocRep_ dr{useInput};
992 dr.Validate (schema);
994 if (callback !=
nullptr) {
995 SAXReader_ handler{*callback};
996 xmlParserCtxtPtr ctxt = xmlCreatePushParserCtxt (&handler.flibXMLSaxHndler_, &handler,
nullptr, 0,
nullptr);
998 [[maybe_unused]]
auto&& cleanup =
Execution::Finally ([&] ()
noexcept { xmlFreeParserCtxt (ctxt); });
999 MyLibXML2StructuredErrGrabber_ errCatcher{ctxt};
1002 useInput.
Seek (*seek2);
1004 while (
auto n = useInput.
ReadBlocking (span{buf}).size ()) {
1005 if (xmlParseChunk (ctxt,
reinterpret_cast<char*
> (buf),
static_cast<int> (n), 0)) {
1008 errCatcher.ThrowIf ();
1010 xmlParseChunk (ctxt,
nullptr, 0, 1);
1011 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 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 >