Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
ObjectReader.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_DataExchange_StructuredStreamEvents_ObjectReader_h_
5#define _Stroika_Foundation_DataExchange_StructuredStreamEvents_ObjectReader_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <optional>
10#include <type_traits>
11#include <typeindex>
12
16#include "Stroika/Foundation/Containers/Adapters/Adder.h"
17#include "Stroika/Foundation/Containers/Bijection.h"
18#include "Stroika/Foundation/Containers/Collection.h"
19#include "Stroika/Foundation/Containers/Mapping.h"
20#include "Stroika/Foundation/Containers/Sequence.h"
21#include "Stroika/Foundation/Containers/Set.h"
23#include "Stroika/Foundation/Containers/SortedMapping.h"
24#include "Stroika/Foundation/Containers/SortedSet.h"
25#include "Stroika/Foundation/DataExchange/StructFieldMetaInfo.h"
27#include "Stroika/Foundation/Memory/Common.h"
33
34#include "IConsumer.h"
35#include "Name.h"
36
37/**
38 *
39 *
40 * \note Code-Status: <a href="Code-Status.md#Alpha">Alpha</a>
41 *
42 * TODO:
43 *
44 * @todo The SimpleReader_ private classes could all be replaced with use of AddCommonReader_Simple()
45 * But the simplereader_ is probably slightly more efficient. Decide if worht the difference or way to
46 * combine these better.
47 *
48 * @todo Extend ObjectReaderRegistry::StructIFNO – with function (like in mixin class) to read stuff –
49 * merge it with mixinhelper – and then use that in all places – class reader, mixin reader, and RepeatedEltReader.
50 *
51 o MixinEltTraits could have overload of Name instead of lambda returning bool,
52 one for case = Name, and either one for case where != Name, or maybe ANY wlidvard
53 with interuendeirng we do first one first.
54
55 --
56
57 o Maybe have MixinEltTraits param for pick subelt more like macro to offset of –
58 soemthign where you pass in just T, eltName and it goes the reinterpret_cast for you.
59
60 --
61
62 redo regtest T8_SAXObjectReader_BLKQCL_ReadAlarms_ - to also use ListOfObjectsReader_NEW
63
64 ----
65
66 FIX DOCS:
67 OptionalTypesReader_ supports reads of optional types. This will work - for any types for
68 * which SimpleReader<T> is implemented.
69 *
70 * Note - this ALWAYS produces a result. Its only called when the element in quesiton has
71 * already occurred. The reaosn for Optional<> pa
72
73 Works for any type where the registry already has a reader for t.
74
75 ----
76 Includein headers example for using objectreaderresgiter
77
78 #if qStroika_Foundation_DataExchange_StructuredStreamEvents_SupportTracing
79 public:
80 bool fTraceThisReader { false }; // very noisy - off by default even for tracemode
81 nonvirtual String TraceLeader_ () const;
82 #endif
83
84----
85
86 * @todo Make AddCommonType() - when passed in an optional<T> - Require that
87 * the type T is already in the registry (like with AddClass). To debug!
88 *
89 * @todo Review names: I don't think we use the term reader and readerfactory totally uniformly, and
90 * we also need to clearly document why/when we use one versus the other (type system stores factories
91 * because you encounter them in parsing, and need instances to pop on stack to read a particular element).
92 *
93 * @todo Need much improved docs
94 *
95 * @todo Get rid of or do differently the Run() methods - so can turn on/off Context debugging easier.
96 *
97 * @todo USE UnknownSubElementDisposition more thoroughly...
98 *
99 * @todo http://stroika-bugs.sophists.com/browse/STK-408 - cleanup template specializations
100 *
101 *
102 * \em Design Overview
103 * This module provides a set of classes to support reading objects from a SAX (event oriented) data source
104 * (https://en.wikipedia.org/wiki/Simple_API_for_XML)
105 *
106 * With SAX parsing, you get a nested series of OPEN TAG and matching CLOSE TAG events, mimicking the structure of
107 * the XML structured document.
108 *
109 * Typically, you will have data structures which roughly map (structurally similarly) to the structure in the XML document
110 * (if not, this module will probably not help you).
111 *
112 * These classes allow you to easily define readers for particular C++ types that correspond to sections of XML (so series
113 * of open/close tag and data in between events).
114 *
115 * Think of each @IElementConsumer subclass (reader) as an object that knows how to read a section of XML (series of open/close
116 * tag events). Predefined classes like @ClassReader<> and @ListOfObjectsReader<> already know how to read certain kinds of XML
117 * and populate likely container objects. You may need to write your own reader class occasionally, but often you can reuse these.
118 *
119 * Then you must be are of how readers are managed. They are created and destroyed as smart_ptr<> objects. And since you often
120 * need to be able to create new ones based on context, they are managed through factories (which create them as the
121 * sax parse encounters particular open/close events).
122 *
123 * You register (in a @Registry) the mapping between a particular type, and its reader factory.
124 *
125 * Because this mapping is sometimes context sensative, Registries can be copied, and are read from a context object.
126 * So in a particular read context, you can replace a particular reader factory for a particular type. Or - within the
127 * ClassReader object, you can provide an optional replacement reader factory for that partiular data member.
128 *
129 */
130
132
133 using Characters::String;
137 using Containers::Set;
138
139 /**
140 */
141#ifndef qStroika_Foundation_DataExchange_StructuredStreamEvents_SupportTracing
142#define qStroika_Foundation_DataExchange_StructuredStreamEvents_SupportTracing qStroika_Foundation_Debug_AssertionsChecked
143#endif
144
145 /**
146 */
147 enum class UnknownSubElementDisposition {
148 eIgnore,
149 eEndObject
150 };
151
152 class Context;
153 class IElementConsumer;
154
155 /**
156 */
157 template <typename T>
158 using ReaderFromTStarFactory = function<shared_ptr<IElementConsumer> (T* destinationObject)>;
159
160 /**
161 * We store in our database factories that read into a 'void*' that must be of the right type,
162 * but we use (at the last minute) the appropriate type. This is typesafe iff the readers do casts
163 * safely (and all the readers we provide do).
164 */
165 using ReaderFromVoidStarFactory = ReaderFromTStarFactory<void>;
166
167 /**
168 * This is just for use the with the ObjectReaderRegistry::AddClass<> (and related) methods, to describe a user-defined type (CLASS).
169 */
171 Name fSerializedFieldName;
172 StructFieldMetaInfo fFieldMetaInfo;
173 optional<ReaderFromVoidStarFactory> fOverrideTypeMapper;
174
175 /**
176 */
177 StructFieldInfo (const Name& serializedFieldName, const StructFieldMetaInfo& fieldMetaInfo,
178 const optional<ReaderFromVoidStarFactory>& typeMapper = nullopt);
179 };
180
181 /**
182 * The basic idea of the ObjectReaderRegistry is to make it easier to write C++ code
183 * to deserialize an XML source (via SAX), into a C++ data structure. This tends to be
184 * MUCH MUCH harder than doing something similar by loading an XML DOM, and then traversing
185 * the DOM with XPath. So why would you do it? This way is dramatically more efficient.
186 * For one thing - there is no need to have the entire source in memory at a time, and there
187 * is no need to ever construct intermediary DOM nodes.
188 *
189 * We need good docs - on how to use this - but for the time being, just look at the
190 * example usage in the regression test.
191 *
192 * Look back to DataExchange::ObjectVariantmapper, but for now - KISS
193 *
194 * \note UNLIKE ObjectVariantReader - the constructor of Registry contains no default readers.
195 *
196 * \par Example Usage
197 * \code
198 * struct Person_ {
199 * String firstName;
200 * String lastName;
201 * };
202 * using namespace ObjectReader;
203 * Registry mapper;
204 * mapper.AddCommonType<String> ();
205 * mapper.AddClass<Person_> ({
206 * { Name { "FirstName" }, &Person_::firstName },
207 * { Name { "LastName" }, &Person_::lastName },
208 * });
209 * Person_ p;
210 * IConsumerDelegateToContext tmpCtx1 (mapper, make_shared<ReadDownToReader> (mapper.MakeContextReader (&p)));
211 * XML::SAXParse (mkdata_ (), tmpCtx1);
212 * \endcode
213 *
214 * \par Example Usage2
215 * @see T3_SAXObjectReader_ReadDown2Sample_ in RegressionTest 'Foundation::DataExchangeFormat::XML::SaxParser'
216 *
217 * \code
218 * .. start with the types and mapper from Example 1, and add
219 * Sequence<Person_> people;
220 * mapper.AddCommonType<Sequence<Person_>> (Name{"WithWhom"});
221 * IConsumerDelegateToContext tmpCtx2 { mapper, make_shared<ReadDownToReader> (newRegistry.MakeContextReader (&people)) };
222 * XML::SAXParse (mkdata_ (), tmp);
223 * \endcode
224 */
225 class Registry {
226 public:
227 /**
228 * \note UNLIKE ObjectVariantReader - the constructor of Registry contains no readers by default.
229 */
230 Registry () = default;
231 Registry (const Registry&) = default;
232
233 public:
234 nonvirtual Registry& operator= (const Registry&) = default;
235
236 public:
237 /**
238 */
239 nonvirtual void Add (type_index forType, const ReaderFromVoidStarFactory& readerFactory);
240 template <typename T>
241 nonvirtual void Add (const ReaderFromTStarFactory<T>& readerFactory);
242
243 public:
244 /**
245 * Shortcut for Add (MakeCommonReader<T> ());
246 *
247 * So - this is supported for any type for which (@see MakeCommonReader) is supported.
248 *
249 * Note this this is not needed (because it's done by default), but is supported,
250 * for the builtin types.
251 */
252 template <typename T, typename... ARGS>
253 nonvirtual void AddCommonType (ARGS&&... args);
254
255 public:
256 nonvirtual optional<ReaderFromVoidStarFactory> Lookup (type_index t) const;
257
258 public:
259 /**
260 * This creates serializers for many common types.
261 * o String
262 * o Time::DateTime
263 * o Time::Duration
264 * o Mapping<Key,Value>
265 * o Optional<T>
266 * o Collection<T>
267 * o Sequence<T>
268 * o vector<T>
269 * o enum types (with eSTART/eEND @see Stroika_Define_Enum_Bounds for bounds checking)
270 * o optional<T> for any type T already in the registry.
271 *
272 * This assumes the template parameters for the above objects are also already defined (mostly 'T' above).
273 *
274 * This function also works (but is generally unneeded for) any of the types defined in
275 * @see ResetToDefaultTypeRegistry () (int, short, String, etc).
276 *
277 * Note - all these de-serializers will throw BadDataFormat exceptions if the data somehow doesn't
278 * fit what the deserailizer expects.
279 *
280 * @see MakeClassReader
281 * @see MakeCommonReader_EnumAsInt
282 * @see MakeCommonReader_NamedEnumerations
283 */
284 template <typename T, typename... ARGS>
285 static ReaderFromVoidStarFactory MakeCommonReader (ARGS&&... args);
286
287 public:
288 /**
289 * \pre AddCommonReader_Class<> requires that each field data type already be pre-loaded into the
290 * ObjectReader::Registry. To avoid this requirement, you an use MakeClassReader
291 * directly, but if this type is absent when you call AddClass<> - its most likely
292 * a bug.
293 */
294 template <typename CLASS>
295 nonvirtual void AddCommonReader_Class (const Traversal::Iterable<StructFieldInfo>& fieldDescriptions);
296
297 public:
298 /**
299 * Create a class factory for a class with the given fields. This does't check (because its static) if the types of the elements in the
300 * fields are part of the mapper.
301 *
302 * @see AddClass<>
303 */
304 template <typename CLASS>
306
307 public:
308 /**
309 * @see MakeCommonReader
310 * @see MakeCommonReader_EnumAsInt
311 */
312 template <typename ENUM_TYPE>
314 template <typename ENUM_TYPE>
316 MakeCommonReader_NamedEnumerations (const Common::EnumNames<ENUM_TYPE>& nameMap = Common::DefaultNames<ENUM_TYPE>::k);
317
318 public:
319 /**
320 * \brief Add reader for any type that is stringish: provide convert function from string to T; Simple wrapper on Add<> and MakeCommonReader_Simple<>
321 *
322 * \par Example Usage
323 * \code
324 * ...
325 * enum class GenderType_ {
326 * Male,
327 * Female,
328 * Stroika_Define_Enum_Bounds (Male, Female)
329 * };
330 * registry.AddCommonReader_NamedEnumerations<GenderType_> (Containers::Bijection<GenderType_, String>{
331 * pair<GenderType_, String>{GenderType_::Male, "Male"},
332 * pair<GenderType_, String>{GenderType_::Female, "Female"},
333 * });
334 * \endcode
335 *
336 * \note if cannot convert, 'converterFromString2T' can/must throw (typically some sort of format exception)
337 */
338 template <typename ENUM_TYPE>
340 template <typename ENUM_TYPE>
341 nonvirtual void AddCommonReader_NamedEnumerations (const Common::EnumNames<ENUM_TYPE>& nameMap = Common::DefaultNames<ENUM_TYPE>::k);
342
343 public:
344 /**
345 * @see MakeCommonReader
346 * @see MakeCommonReader_NamedEnumerations
347 */
348 template <typename ENUM_TYPE>
350
351 public:
352 /**
353 * \brief Add reader for any type that is stringish: provide convert function from string to T; Simple wrapper on Add<> and MakeCommonReader_Simple<>
354 *
355 * \par Example Usage
356 * \code
357 * \par Example Usage
358 * \code
359 * ...
360 * enum class GenderType_ {
361 * Male,
362 * Female,
363 * Stroika_Define_Enum_Bounds (Male, Female)
364 * };
365 * registry.AddCommonReader_EnumAsInt<GenderType_> ();
366 * \endcode
367 */
368 template <typename ENUM_TYPE>
369 nonvirtual void AddCommonReader_EnumAsInt ();
370
371 public:
372 /**
373 * Used to create a reader which maps a string contained in an addressible element (element or attribute) to a value.
374 * Used anywhere you could have used a 'String' reader.
375 *
376 * \note This also ILLUSTATES how easy it is to create readers for more complex types. Just create your own function
377 * like this (clone code) that contains a private class that accomulates data for the stages of SAX parsing and produces
378 * the right type at the end
379 *
380 * \par Example Usage
381 * \code
382 * ...
383 * struct GenderType_ {
384 * String fRep;
385 * };
386 * ...
387 * registry.Add<GenderType_> (registry.MakeCommonReader_Simple< GenderType_> ([](String s) ->GenderType_ { GenderType_ result; result.fRep = s; return result; }));
388 *
389 * \endcode
390 *
391 * \note if cannot convert, 'converterFromString2T' can/must throw (typically some sort of format exception)
392 *
393 * @see MakeCommonReader
394 * @see MakeCommonReader_EnumAsInt
395 */
396 template <typename T>
397 static ReaderFromVoidStarFactory MakeCommonReader_Simple (const function<T (String)>& converterFromString2T);
398
399 public:
400 /**
401 * \brief Add reader for any type that is stringish: provide convert function from string to T; Simple wrapper on Add<> and MakeCommonReader_Simple<>
402 *
403 * \par Example Usage
404 * \code
405 * ...
406 * struct GenderType_ {
407 * String fRep;
408 * };
409 * ...
410 * registry.AddCommonReader_Simple<GenderType_> ([](String s) ->GenderType_ { GenderType_ result; result.fRep = s; return result; });
411 *
412 * \endcode
413 *
414 * \note if cannot convert, 'converterFromString2T' can/must throw (typically some sort of format exception)
415 */
416 template <typename T>
417 nonvirtual void AddCommonReader_Simple (const function<T (String)>& converterFromString2T);
418
419 public:
420 /**
421 * It's pretty easy to code up a 'reader' class - just a simple subclass of IElementConsumer.
422 * There are lots of examples in this module.
423 *
424 * But the APIs in the ObjectReader::Registry require factories, and this utility function constructs
425 * a factory from the given READER class template parameter.
426 */
427 template <typename T, typename READER, typename... ARGS>
428 static auto ConvertReaderToFactory (ARGS&&... args) -> ReaderFromVoidStarFactory;
429
430 public:
431 /**
432 * This is generally just called inside of another composite reader to read sub-objects.
433 */
434 nonvirtual shared_ptr<IElementConsumer> MakeContextReader (type_index ti, void* destinationObject) const;
435 template <typename T>
436 nonvirtual shared_ptr<IElementConsumer> MakeContextReader (T* destinationObject) const;
437
438 private:
439 template <typename T>
440 class SimpleReader_;
441 template <typename T>
442 class OldOptionalTypesReader_;
443
444 private:
445 template <typename T>
446 static ReaderFromVoidStarFactory cvtFactory_ (const ReaderFromTStarFactory<T>& tf);
447
448 private:
449 template <typename T>
450 static ReaderFromVoidStarFactory MakeCommonReader_SimpleReader_ ();
451
452 private:
453 static ReaderFromVoidStarFactory MakeCommonReader_ (const String*);
454 static ReaderFromVoidStarFactory MakeCommonReader_ (const IO::Network::URI*);
455 static ReaderFromVoidStarFactory MakeCommonReader_ (const Time::DateTime*);
456 static ReaderFromVoidStarFactory MakeCommonReader_ (const Time::Duration*);
457 template <typename T>
458 static ReaderFromVoidStarFactory MakeCommonReader_ (const T*)
459 requires (is_enum_v<T>);
460 template <typename T>
461 static ReaderFromVoidStarFactory MakeCommonReader_ (const T*)
462 requires (is_trivially_copyable_v<T> and is_standard_layout_v<T> and not is_enum_v<T>);
463 template <typename T>
464 static ReaderFromVoidStarFactory MakeCommonReader_ (const optional<T>*);
465 template <typename T>
466 static ReaderFromVoidStarFactory MakeCommonReader_ (const vector<T>*);
467 template <typename T>
468 static ReaderFromVoidStarFactory MakeCommonReader_ (const vector<T>*, const Name& name);
469 template <typename T>
470 static ReaderFromVoidStarFactory MakeCommonReader_ (const Collection<T>*);
471 template <typename T>
472 static ReaderFromVoidStarFactory MakeCommonReader_ (const Collection<T>*, const Name& name);
473 template <typename T>
474 static ReaderFromVoidStarFactory MakeCommonReader_ (const Sequence<T>*);
475 template <typename T>
476 static ReaderFromVoidStarFactory MakeCommonReader_ (const Sequence<T>*, const Name& name);
477
478 private:
480 };
481
482 template <>
483 ReaderFromVoidStarFactory Registry::MakeCommonReader_ (const optional<Time::DateTime>*);
484
485 /**
486 * Subclasses of this abstract class are responsible for consuming data at a given level
487 * of the SAX 'tree', and transform it into a related object.
488 *
489 * These are generally 'readers' of input XML, and writers of inflated objects.
490 */
491 class IElementConsumer : public enable_shared_from_this<IElementConsumer> {
492 protected:
493 /**
494 */
495 IElementConsumer () = default;
496
497 public:
498 /**
499 */
500 virtual ~IElementConsumer () = default;
501
502 public:
503 /**
504 * Pushed onto context stack.
505 *
506 * Once activated, an element cannot be Activated () again until its been deactivated.
507 * All elements that have been Activated () must be de-activated to disgorge/flush their work.
508 *
509 * @see Deactivating
510 */
511 virtual void Activated (Context& r);
512
513 public:
514 /**
515 */
516 virtual shared_ptr<IElementConsumer> HandleChildStart (const Name& name);
517
518 public:
519 /**
520 * NOTE that this may be called zero or more times between Activated and Deactivating, and generally
521 * all the text should be accumulated. Callers may split incoming text into arbitrarily many pieces.
522 */
523 virtual void HandleTextInside (const String& text);
524
525 public:
526 /**
527 * About to pop from the stack.
528 *
529 * @see Activated
530 */
531 virtual void Deactivating ();
532
533 public:
534 /**
535 * Helper to convert a reader to a factory (something that creates the reader).
536 *
537 * Equivalent to
538 * Registry::ConvertReaderToFactory<TARGET_TYPE, READER> ()
539 *
540 * Typically shadowed in subclasses (actual readers) to fill in default parameters.
541 */
542 template <typename TARGET_TYPE, typename READER, typename... ARGS>
543 static ReaderFromVoidStarFactory AsFactory (ARGS&&... args);
544 };
545
546 /**
547 * This concrete class is used to capture the state of an ongoing StructuredStreamParse/transformation. Logically, it
548 * maintains the 'stack' you would have in constructing a recursive decent object mapping.
549 *
550 * \note Important Design Principle for Reader
551 *
552 * We only recognize a new 'type' by its 'Start' element. So its at that (or sometimes the start/end of a child) that
553 * we can PUSH a new reader in place.
554 *
555 * The current tags and text get delivered to the top of the stack.
556 *
557 * When we encounter a close tag, we end that reading, and so pop.
558 *
559 * This means that the start and end tags for a given pair, go to different 'IElementConsumer'
560 * subclasses. The START goes to the parent (so it can create the right type), and the EndTag
561 * goes to the created/pushed type, so it can close itself and pop back to the parent context.
562 *
563 * \note Debugging Note
564 * It's often helpful to turn on fTraceThisReader while debugging SAX parsing, to see why something
565 * isn't being read.
566 */
567 class Context {
568 public:
569#if qStroika_Foundation_DataExchange_StructuredStreamEvents_SupportTracing
570 public:
571 bool fTraceThisReader{false}; // very noisy - off by default even for tracemode
572 nonvirtual String TraceLeader_ () const;
573#endif
574
575 public:
576 /**
577 */
578 Context (const Registry& registry);
579 Context (const Registry& registry, const shared_ptr<IElementConsumer>& initialTop);
580 Context (const Context&) = delete;
581 Context (Context&& from) noexcept;
582 nonvirtual Context& operator= (const Context&) = delete;
583
584 public:
585 nonvirtual const Registry& GetObjectReaderRegistry () const;
586
587 public:
588 nonvirtual void Push (const shared_ptr<IElementConsumer>& elt);
589
590 public:
591 nonvirtual void Pop ();
592
593 public:
594 nonvirtual shared_ptr<IElementConsumer> GetTop () const;
595
596 public:
597 nonvirtual bool empty () const;
598
599 private:
600 const Registry& fObjectReaderRegistry_;
601 vector<shared_ptr<IElementConsumer>> fStack_;
602
603 private:
604 friend class ObjectReader;
605 };
606
607 /**
608 * @see Registry for examples of use
609 */
611 public:
612 IConsumerDelegateToContext () = delete;
614 IConsumerDelegateToContext (const Registry& registry);
615 IConsumerDelegateToContext (const Registry& registry, const shared_ptr<IElementConsumer>& initialTop);
617 IConsumerDelegateToContext& operator= (const IConsumerDelegateToContext&) = delete;
618
619 public:
620 virtual void StartElement (const Name& name, const Mapping<Name, String>& attributes) override;
621 virtual void EndElement (const Name& name) override;
622 virtual void TextInsideElement (const String& text) override;
623
624 public:
625 Context fContext;
626 };
627
628 /**
629 * Push one of these Nodes onto the stack to handle 'reading' a node which is not to be read.
630 * This is necessary to balance out the Start Tag / End Tag combinations.
631 */
633 public:
634 /**
635 * Note the IGNORED* overload is so this can be used generically with a factory (though no clear reason
636 * I can think of why you would want to).
637 */
638 IgnoreNodeReader () = default;
639
640 public:
641 virtual shared_ptr<IElementConsumer> HandleChildStart (const Name& name) override;
642
643 public:
644 /**
645 * Helper to convert a reader to a factory (something that creates the reader).
646 */
648 };
649
650 /**
651 */
652 template <typename T>
653 class ClassReader : public IElementConsumer {
654 public:
655 ClassReader (const Traversal::Iterable<StructFieldInfo>& fieldDescriptions, T* vp);
656
657 public:
658 virtual void Activated (Context& r) override;
659 virtual shared_ptr<IElementConsumer> HandleChildStart (const Name& name) override;
660 virtual void HandleTextInside (const String& text) override;
661 virtual void Deactivating () override;
662
663 public:
664 /**
665 * Helper to convert a reader to a factory (something that creates the reader).
666 */
667 static ReaderFromVoidStarFactory AsFactory ();
668
669 private:
670 nonvirtual ReaderFromVoidStarFactory LookupFactoryForName_ (const Name& name) const;
671
672 private:
673 Traversal::Iterable<StructFieldInfo> fFieldDescriptions_;
674 Context* fActiveContext_{};
675 T* fValuePtr_{};
676 Mapping<Name, StructFieldMetaInfo> fFieldNameToTypeMap_;
677 optional<StructFieldMetaInfo> fValueFieldMetaInfo_;
678 shared_ptr<IElementConsumer> fValueFieldConsumer_;
679 bool fThrowOnUnrecongizedelts_{false}; // else ignore
680 };
681
682 /**
683 * Eat/ignore everything down the level named by 'tagToHandoff'.
684 * If tagToHandoff is missing, start on the first element.
685 * Note - its not an error to call theUseReader with multiple starts, but never two at a time.
686 *
687 * \note - the tag == tagToHandoff will be handed to theUseReader.
688 *
689 * \note If multiple elements match, each one is started with theUseReader
690 */
692 public:
693 ReadDownToReader (const shared_ptr<IElementConsumer>& theUseReader);
694 ReadDownToReader (const shared_ptr<IElementConsumer>& theUseReader, const Name& tagToHandOff);
695 ReadDownToReader (const shared_ptr<IElementConsumer>& theUseReader, const Name& contextTag, const Name& tagToHandOff);
696 ReadDownToReader (const shared_ptr<IElementConsumer>& theUseReader, const Name& contextTag1, const Name& contextTag2, const Name& tagToHandOff);
697
698 public:
699 virtual shared_ptr<IElementConsumer> HandleChildStart (const Name& name) override;
700
701 public:
702 /**
703 * Helper to convert a reader to a factory (something that creates the reader).
704 */
706 static ReaderFromVoidStarFactory AsFactory (const ReaderFromVoidStarFactory& theUseReader, const Name& tagToHandOff);
707
708 private:
709 shared_ptr<IElementConsumer> fReader2Delegate2_;
710 optional<Name> fTagToHandOff_;
711 };
712
713 /**
714 * ListOfObjectReader<> can read a container (vector-like) of elements. You can optionally specify
715 * the name of each element, or omit that, to assume every sub-element is of the 'T' type.
716 *
717 * This is just like Repeated except it starts exactly one 'xml' level up from the target element.
718 *
719 * This reader reads structured elements ('xml') content like:
720 * <list>
721 * <elt>
722 * <elt>
723 * ...
724 * </list>
725 * into some sort of sequenced container (like vector or sequence).
726 *
727 * This is very similar to RepeatedElementReader (and uses it internally) - except that this starts
728 * one level higher in teh Structured Element Stream (xml).
729 *
730 * @see RepeatedElementReader
731 */
732 template <typename CONTAINER_OF_T>
734 public:
735 using ElementType = typename CONTAINER_OF_T::value_type;
736
737 public:
738 ListOfObjectsReader (CONTAINER_OF_T* v);
739 ListOfObjectsReader (CONTAINER_OF_T* v, const Name& memberElementName);
740
741 public:
742 virtual void Activated (Context& r) override;
743 virtual shared_ptr<IElementConsumer> HandleChildStart (const Name& name) override;
744 virtual void Deactivating () override;
745
746 public:
747 /**
748 * Helper to convert a reader to a factory (something that creates the reader).
749 */
751 static ReaderFromVoidStarFactory AsFactory (const Name& memberElementName);
752
753 private:
754 CONTAINER_OF_T* fValuePtr_{};
755 Context* fActiveContext_{};
756 optional<Name> fMemberElementName_;
757 bool fThrowOnUnrecongizedelts_{false};
758 };
759
760 /**
761 */
762 template <typename T>
763 struct OptionalTypesReader_DefaultTraits {
764 static inline const T kDefaultValue = T{};
765 };
766
767 /**
768 * \brief Reader for optional<X> types - typically just use AddCommonType<optional<T>> () - but use this if you need to parameterize
769 *
770 * OptionalTypesReader supports reads of optional types. This will work - for any types for
771 * which SimpleReader<T> is implemented.
772 *
773 * Note - this ALWAYS produces a result. Its only called when the element in quesiton has
774 * already occurred. The reason for Optional<> part is because the caller had an optional
775 * element which might never have triggered the invocation of this class.
776 */
777 template <typename T, typename TRAITS = OptionalTypesReader_DefaultTraits<T>>
779 public:
780 OptionalTypesReader (optional<T>* intoVal);
781
782 public:
783 virtual void Activated (Context& r) override;
784 virtual shared_ptr<IElementConsumer> HandleChildStart (const Name& name) override;
785 virtual void HandleTextInside (const String& text) override;
786 virtual void Deactivating () override;
787
788 public:
789 /**
790 * Helper to convert a reader to a factory (something that creates the reader).
791 */
793
794 private:
795 optional<T>* fValue_{};
796 T fProxyValue_{TRAITS::kDefaultValue};
797 shared_ptr<IElementConsumer> fActualReader_{};
798 };
799
800 /**
801 */
802 template <typename T>
803 class MixinReader : public IElementConsumer {
804 public:
805 struct MixinEltTraits {
806 ReaderFromVoidStarFactory fReaderFactory;
807 function<bool (const Name& name)> fReadsName = [] (const Name& name) { return true; };
808 function<bool ()> fReadsText = [] () { return true; };
809 function<byte*(T*)> fAddressOfSubElementFetcher;
810
811 static const function<byte*(T*)> kDefaultAddressOfSubElementFetcher;
812
813 MixinEltTraits (const ReaderFromVoidStarFactory& readerFactory,
814 const function<byte*(T*)>& addressOfSubEltFetcher = kDefaultAddressOfSubElementFetcher);
815 MixinEltTraits (const ReaderFromVoidStarFactory& readerFactory, const function<bool (const Name& name)>& readsName,
816 const function<byte*(T*)>& addressOfSubEltFetcher = kDefaultAddressOfSubElementFetcher);
817 MixinEltTraits (const ReaderFromVoidStarFactory& readerFactory, const function<bool ()>& readsText,
818 const function<byte*(T*)>& addressOfSubEltFetcher = kDefaultAddressOfSubElementFetcher);
819 MixinEltTraits (const ReaderFromVoidStarFactory& readerFactory, const function<bool (const Name& name)>& readsName,
820 const function<bool ()>& readsText, const function<byte*(T*)>& addressOfSubEltFetcher = kDefaultAddressOfSubElementFetcher);
821 };
822
823 public:
824 /**
825 */
826 MixinReader (T* vp, const Traversal::Iterable<MixinEltTraits>& mixins);
827
828 public:
829 virtual void Activated (Context& r) override;
830 virtual shared_ptr<IElementConsumer> HandleChildStart (const Name& name) override;
831 virtual void HandleTextInside (const String& text) override;
832 virtual void Deactivating () override;
833
834 public:
835 /**
836 * Helper to convert a reader to a factory (something that creates the reader).
837 */
838 static ReaderFromVoidStarFactory AsFactory (const Traversal::Iterable<MixinEltTraits>& mixins);
839
840 private:
841 Context* fActiveContext_{};
842 T* fValuePtr_{};
843 Sequence<MixinEltTraits> fMixins_;
844 Sequence<shared_ptr<IElementConsumer>> fMixinReaders_;
845 Set<shared_ptr<IElementConsumer>> fActivatedReaders_;
846 };
847
848 /**
849 * \note This is a public class instead of accessed via MakeCommonReader<T> since its very common that it will need
850 * extra parameters (specified through the CTOR/and can be specified through the AsFactory method) to specify the name elements
851 * to use for bounds attributes.
852 */
853 template <typename T>
855 public:
856 static const inline pair<Name, Name> kDefaultBoundsNames{Name{"LowerBound"sv, Name::eAttribute}, Name{"UpperBound"sv, Name::eAttribute}};
857
858 public:
859 /**
860 */
861 RangeReader (T* intoVal, const pair<Name, Name>& pairNames = kDefaultBoundsNames);
862
863 public:
864 virtual void Activated (Context& r) override;
865 virtual shared_ptr<IElementConsumer> HandleChildStart (const Name& name) override;
866 virtual void HandleTextInside (const String& text) override;
867 virtual void Deactivating () override;
868
869 public:
870 /**
871 * Helper to convert a reader to a factory (something that creates the reader).
872 */
873 static ReaderFromVoidStarFactory AsFactory (const pair<Name, Name>& pairNames = kDefaultBoundsNames);
874
875 private:
876 using range_value_type_ = typename T::value_type;
877 struct RangeData_ {
878 range_value_type_ fLowerBound{};
879 range_value_type_ fUpperBound{};
880 };
881 pair<Name, Name> fPairNames;
882 T* fValue_{};
883 RangeData_ fProxyValue_{};
884 shared_ptr<IElementConsumer> fActualReader_{};
885 };
886
887 /**
888 * This is kind of like optional, but its for sequence elements - elements that are repeated inline.
889 *
890 * To use RepeatedElementReader, just add the type to the type registry, and use it as normal for a regular field.
891 * Only declare the field as a vector or Sequence <T>. This will invoke the right 'T' reader each time the name
892 * is encountered, but instead of storing the value, it will append the value to the named object.
893 *
894 * \par Example Usage
895 * \code
896 * // EXAMLPE DATA:
897 * <envelope1>
898 * <person>
899 * <FirstName>Jim</FirstName>
900 * <LastName>Smith</LastName>
901 * </person>
902 * <person>
903 * <FirstName>Fred</FirstName>
904 * <LastName>Down</LastName>
905 * </person>
906 * <address>
907 * <city>Boston</city>
908 * <state>MA</state>
909 * </address>
910 * <address>
911 * <city>New York</city>
912 * <state>NY</state>
913 * </address>
914 * <address>
915 * <city>Albany</city>
916 * <state>NY</state>
917 * </address>
918 * </envelope1>
919 * struct Person_ {
920 * String firstName;
921 * String lastName;
922 * };
923 * struct Address_ {
924 * String city;
925 * String state;
926 * };
927 * struct Data_ {
928 * vector<Person_> people;
929 * vector<Address_> addresses;
930 * };
931 *
932 * using namespace ObjectReader;
933 * Registry registry;
934 * registry.AddCommonType<String> ();
935 * registry.AddClass<Person_> ({
936 * { Name { "FirstName" }, &Person_::firstName },
937 * { Name { "LastName" }, &Person_::lastName },
938 * });
939 * registry.AddCommonType<vector<Person_>> ();
940 * registry.Add<vector<Person_>> (Registry::ConvertReaderToFactory <vector<Person_>, RepeatedElementReader<vector<Person_>>> ());
941 * registry.AddClass<Address_> ({
942 * { Name { "city" }, &Address_::city },
943 * { Name { "state" }, &Address_::state },
944 * });
945 * registry.Add<vector<Address_>> (Registry::ConvertReaderToFactory <vector<Address_>, RepeatedElementReader<vector<Address_>>> ());
946 * registry.AddClass<Data_> ({
947 * { Name { "person" }, &Data_::people },
948 * { Name { "address" }, &Data_::addresses },
949 * });
950 * Data_ data;
951 * Registry::IConsumerDelegateToContext ctx { registry, make_shared<ReadDownToReader> (registry.MakeContextReader (&data)) };
952 * XML::SAXParse (srcXMLStream, ctx);
953 * \endcode
954 *
955 * \note This is like @see ListOfElementsReader, except that it starts on the elements of the array itself, as opposed
956 * to just above.
957 */
958 template <typename CONTAINER_OF_T, typename CONTAINER_ADAPTER_ADDER = Containers::Adapters::Adder<CONTAINER_OF_T>>
960 using ContainerAdapterAdder = CONTAINER_ADAPTER_ADDER;
961 using TValueType = typename CONTAINER_OF_T::value_type;
962 static inline const TValueType kDefaultValue = TValueType{};
963 };
964 template <typename T, typename TRAITS = RepeatedElementReader_DefaultTraits<T>>
965 class RepeatedElementReader : public IElementConsumer {
966 public:
967 using ContainerType = T;
968 using ElementType = typename ContainerType::value_type;
969
970 public:
971 /**
972 * If name specified, pass only that named subelelement to subeltreader.
973 *
974 * If actualElementFactory specified, use that to create subelement reader, and otherwise use
975 * Context::GetObjectReaderRegistry ().MakeContextReader ()
976 */
977 RepeatedElementReader (ContainerType* v);
978 RepeatedElementReader (ContainerType* v, const Name& readonlyThisName, const ReaderFromVoidStarFactory& actualElementFactory);
979 RepeatedElementReader (ContainerType* v, const ReaderFromVoidStarFactory& actualElementFactory);
980 RepeatedElementReader (ContainerType* v, const Name& readonlyThisName);
981
982 public:
983 virtual void Activated (Context& r) override;
984 virtual shared_ptr<IElementConsumer> HandleChildStart (const Name& name) override;
985 virtual void HandleTextInside (const String& text) override;
986 virtual void Deactivating () override;
987
988 public:
989 /**
990 * Helper to convert a reader to a factory (something that creates the reader).
991 */
992 static ReaderFromVoidStarFactory AsFactory ();
993 static ReaderFromVoidStarFactory AsFactory (const Name& readonlyThisName, const ReaderFromVoidStarFactory& actualElementFactory);
994 static ReaderFromVoidStarFactory AsFactory (const ReaderFromVoidStarFactory& actualElementFactory);
995 static ReaderFromVoidStarFactory AsFactory (const Name& readonlyThisName);
996
997 private:
998 ContainerType* fValuePtr_{};
999 optional<ReaderFromVoidStarFactory> fReaderRactory_{}; // if missing, use Context::GetObjectReaderRegistry ().MakeContextReader ()
1000 function<bool (Name)> fReadThisName_{[] ([[maybe_unused]] const Name& n) { return true; }};
1001 ElementType fProxyValue_{TRAITS::kDefaultValue};
1002 shared_ptr<IElementConsumer> fActiveSubReader_{};
1003 };
1004
1005 /**
1006 */
1007 [[noreturn]] void ThrowUnRecognizedStartElt (const Name& name);
1008
1009}
1010
1011/*
1012 ********************************************************************************
1013 ***************************** Implementation Details ***************************
1014 ********************************************************************************
1015 */
1016#include "ObjectReader.inl"
1017
1018#endif /*_Stroika_Foundation_DataExchange_StructuredStreamEvents_ObjectReader_h_*/
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
Bijection allows for the bijective (1-1) association of two elements.
Definition Bijection.h:103
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
Definition Collection.h:102
A generalization of a vector: a container whose elements are keyed by the natural numbers.
Definition Sequence.h:187
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
Definition Set.h:105
Reader for optional<X> types - typically just use AddCommonType<optional<T>> () - but use this if you...
static ReaderFromVoidStarFactory AsFactory(const pair< Name, Name > &pairNames=kDefaultBoundsNames)
static ReaderFromVoidStarFactory AsFactory(const ReaderFromVoidStarFactory &theUseReader)
nonvirtual void AddCommonReader_NamedEnumerations(const Containers::Bijection< ENUM_TYPE, String > &nameMap)
Add reader for any type that is stringish: provide convert function from string to T; Simple wrapper ...
static ReaderFromVoidStarFactory MakeClassReader(const Traversal::Iterable< StructFieldInfo > &fieldDescriptions)
nonvirtual void AddCommonReader_Class(const Traversal::Iterable< StructFieldInfo > &fieldDescriptions)
static ReaderFromVoidStarFactory MakeCommonReader_Simple(const function< T(String)> &converterFromString2T)
nonvirtual void AddCommonReader_Simple(const function< T(String)> &converterFromString2T)
Add reader for any type that is stringish: provide convert function from string to T; Simple wrapper ...
static auto ConvertReaderToFactory(ARGS &&... args) -> ReaderFromVoidStarFactory
nonvirtual void AddCommonReader_EnumAsInt()
Add reader for any type that is stringish: provide convert function from string to T; Simple wrapper ...
nonvirtual shared_ptr< IElementConsumer > MakeContextReader(type_index ti, void *destinationObject) const
static ReaderFromVoidStarFactory MakeCommonReader_NamedEnumerations(const Containers::Bijection< ENUM_TYPE, String > &nameMap)
virtual void StartElement(const Name &name, const Mapping< Name, String > &attributes) override
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237