4#include "Stroika/Foundation/StroikaPreComp.h"
9#include "Stroika/Foundation/Characters/FloatConversion.h"
12#include "Stroika/Foundation/Containers/Mapping.h"
13#include "Stroika/Foundation/Containers/Sequence.h"
14#include "Stroika/Foundation/Containers/Set.h"
15#include "Stroika/Foundation/DataExchange/BadFormatException.h"
18#include "Stroika/Foundation/Execution/Exceptions.h"
19#include "Stroika/Foundation/Execution/Throw.h"
23#include "Stroika/Foundation/Streams/TextToBinary.h"
35using namespace Stroika::Foundation::DataExchange::XML;
36using namespace Stroika::Foundation::DataExchange::Variant::XML;
39using Memory::MakeSharedPtr;
44#if qStroika_Foundation_DataExchange_XML_SupportParsing
50 auto isElement = [] (
Binding::Kind k) {
return k == Binding::Kind::eElement or k == Binding::Kind::ePresenceMarker; };
53 return (isElement (kind) ? isElement (k) : k == kind) and binding->NameFor (m.fKey).fName == name;
62 if (binding ==
nullptr) {
63 return Binding::UndeclaredMembers::eKeep;
65 if (binding->
fType and binding->
fType != VariantValue::eMap) {
66 if (binding->
fType == VariantValue::eArray and binding->
fUndeclaredMembers == Binding::UndeclaredMembers::eSkip) {
67 return Binding::UndeclaredMembers::eSkip;
69 return Binding::UndeclaredMembers::eReject;
74 template <
typename INT>
75 optional<INT> ParseInteger_ (
const String& text)
79 auto [ptr, ec] = from_chars (s->data (), s->data () + s->size (), r);
80 if (ec == errc{} and ptr == s->data () + s->size ()) {
93 case VariantValue::eString:
95 case VariantValue::eBoolean:
97 if (trimmed ==
"true"sv or trimmed ==
"1"sv) {
100 if (trimmed ==
"false"sv or trimmed ==
"0"sv) {
104 case VariantValue::eInteger:
105 if (optional<long long> i = ParseInteger_<long long> (trimmed)) {
109 case VariantValue::eUnsignedInteger:
110 if (optional<unsigned long long> i = ParseInteger_<unsigned long long> (trimmed)) {
114 case VariantValue::eFloat: {
115 long double d = FloatConversion::ToFloat<long double> (trimmed);
120 case VariantValue::eDate:
122 case VariantValue::eDateTime:
123 return VariantValue{Time::DateTime::Parse (trimmed, Time::DateTime::kISO8601Format)};
124 case VariantValue::eBLOB:
141 Builder_ (
const Binding& binding,
bool lenient)
154 return AsDeclaredType_ (text, type);
160 return AsDeclaredType_ (text, type);
166 const Binding* fBinding{
nullptr};
169 bool fIsRepeatedItem{
false};
170 bool fIsBoundArray{
false};
171 bool fHasChildElements{
false};
172 bool fIsPresenceMarker{
false};
173 optional<String> fItemName;
174 const Binding* fItemBinding{
nullptr};
180 vector<Frame_> fStack;
182 optional<VariantValue> fRootValue;
183 unsigned int fSkipDepth_{0};
187 if (fSkipDepth_ > 0) {
193 const Binding* ownBinding =
nullptr;
194 f.fElement = name.fLocalName;
195 if (fStack.empty ()) {
196 if (fBinding.
fName) {
199 if (name.fLocalName != fBinding.
fName->fName) {
201 "the document element is <{}>, where the Binding names <{}>"_f(name.fLocalName, fBinding.
fName->fName)});
203 ownBinding = &fBinding;
207 f.fKey = fBinding.
KeyForName (Binding::Kind::eElement, name.fLocalName);
215 const Frame_& parent = fStack.back ();
216 f.fDefaultArrayItems = parent.fDefaultArrayItems;
217 if (parent.fItemName and name.fLocalName == *parent.fItemName and
218 not NamesMember_ (parent.fBinding, Binding::Kind::eElement, name.fLocalName)) {
220 ownBinding = parent.fItemBinding;
224 if (not NamesMember_ (parent.fBinding, Binding::Kind::eElement, name.fLocalName)) {
225 switch (UndeclaredIn_ (parent.fBinding)) {
226 case Binding::UndeclaredMembers::eReject:
231 case Binding::UndeclaredMembers::eSkip:
234 case Binding::UndeclaredMembers::eKeep:
238 f.fKey = parent.fBinding ==
nullptr ? name.fLocalName : parent.fBinding->KeyForName (Binding::Kind::eElement, name.fLocalName);
239 ownBinding = parent.fBinding ==
nullptr ? nullptr : parent.fBinding->MemberBinding (f.fKey);
247 const Binding* items =
nullptr;
248 if (ownBinding !=
nullptr) {
249 items = ownBinding->
fItems !=
nullptr ? ownBinding->
fItems.get ()
250 : ownBinding->
fType == VariantValue::eArray ? f.fDefaultArrayItems
253 f.fBinding = ownBinding;
254 f.fIsPresenceMarker = ownBinding !=
nullptr and ownBinding->fKind == Binding::Kind::ePresenceMarker;
255 if (items !=
nullptr and not items->
fName) {
258 f.fIsRepeatedItem =
true;
261 if (f.fBinding != ownBinding and f.fBinding->fDefaultArrayItems !=
nullptr) {
262 f.fDefaultArrayItems = f.fBinding->fDefaultArrayItems.get ();
266 bool declaresNoArray = f.fBinding !=
nullptr and f.fBinding->fType and f.fBinding->fType != VariantValue::eArray;
267 if (f.fBinding !=
nullptr and f.fBinding->fItems !=
nullptr) {
268 if (f.fBinding->fItems->fName) {
269 f.fIsBoundArray =
true;
270 f.fItemName = f.fBinding->fItems->fName->fName;
271 f.fItemBinding = f.fBinding->fItems.get ();
274 else if (f.fDefaultArrayItems->fName and not declaresNoArray) {
275 f.fItemName = f.fDefaultArrayItems->fName->fName;
276 f.fItemBinding = f.fDefaultArrayItems;
278 fStack.push_back (move (f));
279 Frame_& top = fStack.back ();
280 for (
const auto& i : attributes) {
281 if (not NamesMember_ (top.fBinding, Binding::Kind::eAttribute, i.fKey.fLocalName)) {
282 switch (UndeclaredIn_ (top.fBinding)) {
283 case Binding::UndeclaredMembers::eReject:
286 "<{}>'s attribute '{}' is not one its Binding declares"_f(name.fLocalName, i.fKey.fLocalName)});
289 case Binding::UndeclaredMembers::eSkip:
291 case Binding::UndeclaredMembers::eKeep:
295 String key = top.fBinding ==
nullptr ? i.fKey.fLocalName : top.fBinding->KeyForName (Binding::Kind::eAttribute, i.fKey.fLocalName);
296 const Binding* attrBinding = top.fBinding ==
nullptr ? nullptr : top.fBinding->MemberBinding (key);
297 if (attrBinding ==
nullptr or not attrBinding->
fType) {
300 else if (optional<VariantValue> v = Declared_ (i.fValue, *attrBinding->
fType)) {
301 AddMember_ (top, key, *v);
307 if (not fStack.empty () and fSkipDepth_ == 0) {
308 fStack.back ().fText << text;
311 virtual void EndElement ([[maybe_unused]]
const Name& name)
override
313 if (fSkipDepth_ > 0) {
317 Require (not fStack.empty ());
318 Frame_ f = move (fStack.back ());
320 optional<VariantValue> v = ValueOf_ (f);
321 if (fStack.empty ()) {
327 Frame_& parent = fStack.back ();
328 parent.fHasChildElements =
true;
333 parent.fArrayItems.Append (*v);
335 else if (f.fIsRepeatedItem) {
338 if (optional<VariantValue> already = parent.fChildren.Lookup (f.fKey)) {
345 AddMember_ (parent, f.fKey, *v);
353 if (optional<VariantValue> already = f.fChildren.Lookup (key)) {
354 if (f.fBinding !=
nullptr and f.fBinding->fType) {
356 f.fChildren.Add (key, v);
359 Execution::Throw (
BadFormatException{
"<{}> has '{}' more than once, where its Binding declares one"_f(f.fElement, key)});
364 f.fRepeated.Add (key);
367 f.fChildren.Add (key, v);
371 optional<VariantValue> ValueOf_ (Frame_& f)
373 String text = f.fText.str ();
374 bool textBlank = text.
Trim ().empty ();
375 optional<VariantValue::Type> declared = f.fBinding ==
nullptr ? nullopt : f.fBinding->fType;
376 if (f.fIsPresenceMarker) {
378 if (not fLenient and (not f.fChildren.empty () or f.fHasChildElements or not textBlank)) {
379 Execution::Throw (
BadFormatException{
"<{}> is a presence marker (Binding::Kind::ePresenceMarker), so an empty element"_f(f.fElement)});
383 if (declared and *declared != VariantValue::eMap) {
385 Assert (*declared == VariantValue::eArray or f.fChildren.empty ());
386 if (*declared == VariantValue::eArray) {
387 if (not fLenient and (not f.fChildren.empty () or not textBlank)) {
388 Execution::Throw (
BadFormatException{
"<{}> is not an array, as its Binding declares"_f(f.fElement)});
392 return Declared_ (text, *declared);
394 if (f.fIsBoundArray and f.fChildren.empty () and textBlank) {
397 if (not f.fArrayItems.empty () and f.fChildren.empty () and textBlank) {
400 if (not f.fArrayItems.empty ()) {
402 AddMember_ (f, *f.fItemName,
VariantValue{f.fArrayItems});
403 f.fArrayItems.clear ();
406 if (f.fBinding !=
nullptr) {
408 if (f.fBinding->KindFor (m.fKey) == Binding::Kind::ePresenceMarker and not f.fChildren.ContainsKey (m.fKey)) {
413 static const auto kMixedContent_ =
BadFormatException{
"mixed content (text beside child elements) cannot be read into a VariantValue"sv};
416 if (optional<String> textKey = f.fBinding ==
nullptr ? nullopt : f.fBinding->TextMemberKey ()) {
417 if (f.fHasChildElements) {
420 if (not textBlank and not fLenient) {
421 Execution::Throw (kMixedContent_);
425 const Binding* textBinding = f.fBinding->MemberBinding (*textKey);
426 if (textBinding ==
nullptr or not textBinding->
fType) {
429 else if (not text.empty ()) {
430 if (optional<VariantValue> v = Declared_ (text, *textBinding->
fType)) {
431 f.fChildren.Add (*textKey, *v);
436 if (f.fChildren.empty () and declared != VariantValue::eMap) {
442 if (f.fChildren.empty () and not textBlank) {
443 Execution::Throw (
BadFormatException{
"<{}> has text, where its Binding declares an object"_f(f.fElement)});
448 if (f.fHasChildElements) {
449 Execution::Throw (kMixedContent_);
452 "text beside attributes cannot be read into a VariantValue with no Binding member for it (Binding::Kind::eText)"sv};
453 Execution::Throw (kTextBesideAttributes_);
469 virtual optional<filesystem::path> GetDefaultFileSuffix ()
const override
475#if qStroika_Foundation_DataExchange_XML_SupportParsing
476 Builder_ builder{fOptions_.fBinding, fOptions_.fLenient};
477 SAXParse (in, &builder, fOptions_.fValidateAgainst,
nullptr, fOptions_.fXMLOptions);
478 if (not builder.fRootValue) {
480 Execution::Throw (kException_);
486 *builder.fRootValue }
490 "Variant::XML::Reader requires an XML parser (qStroika_Foundation_DataExchange_XML_SupportParsing)"sv};
491 Execution::Throw (kException_);
497 return Read (Streams::TextToBinary::Reader::New (in));
504#if qCompilerAndStdLib_default_member_initializer_needed_within_enclosing_class_Buggy
505Variant::XML::Reader::Reader ()
511Variant::XML::Reader::Reader (
const Options& options)
516Variant::XML::Reader::Reader (
const Binding& binding)
auto MakeSharedPtr(ARGS_TYPE &&... args) -> shared_ptr< T >
same as make_shared, but if type T has block allocation, then use block allocation for the 'shared pa...
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.
void SAXParse(const Providers::ISAXProvider &saxProvider, const Streams::InputStream::Ptr< byte > &in, StructuredStreamEvents::IConsumer *callback=nullptr, const Schema::Ptr &schema=nullptr, Execution::ProgressMonitor::Updater progress=nullptr)
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual String ToLowerCase() const
nonvirtual String Trim(bool(*shouldBeTrimmed)(Character)=Character::IsWhitespace) const
nonvirtual optional< T > AsASCIIQuietly() const
nonvirtual optional< size_t > Find(Character c, CompareOptions co=eWithCase) const
A Mapping uniquely associates two elements: a key and a value (use Assocation to allow multiple value...
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual void As(CONTAINER_OF_ADDABLE *into) const
write this Sequence into an existing container
nonvirtual void Append(ArgByValueType< value_type > item)
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
virtual void TextInsideElement(const String &text)
virtual void EndElement(const Name &name)
virtual void StartElement(const Name &name, const Mapping< Name, String > &attributes)
Read XML as a VariantValue - the inverse of Variant::XML::Writer, whose documentation has the shape.
Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in ...
Type
Enumeration of variant types.
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
static constexpr string_view kISO8601Format
Y-M-D format - locale independent, and ISO-8601 date format standard.
static Date Parse(const String &rep, const locale &l=locale{})
void Throw(T &&e2Throw)
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,...
One member of an object: its key, and how that member differs from the default shape (nothing,...
How a VariantValue is to be written as XML, and read back, where the default shape (.
nonvirtual String KeyForName(Kind kind, const String &name) const
Reading: the key of the member of that kind (an element - or presence marker - or an attribute) writt...
static const Binding kDefaultArrayItems
The Binding each item of an array is written with where neither fItems nor fDefaultArrayItems gives o...
Sequence< Member > fMembers
nonvirtual Kind KindFor(const String &key) const
That member's kind: its Binding's, else an element.
optional< VariantValue::Type > fType
nonvirtual UndeclaredMembers GetUndeclaredMembers() const
fUndeclaredMembers, or its default: eReject where fType is declared, eKeep where not
UndeclaredMembers
What an object does with a member fMembers does not declare - an element or attribute in a document,...
shared_ptr< Binding > fItems
optional< NameWithNamespace > fName
nonvirtual const Binding * MemberBinding(const String &key) const
The Binding for the member of this object with that key - nullptr where it has none,...
shared_ptr< Binding > fDefaultArrayItems
The Binding each item of an array is written with, where the array's own Binding has no fItems: its f...
optional< UndeclaredMembers > fUndeclaredMembers
How XML is read and written - for now, with which provider - taken last by each XML function (DOM::Do...