-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathxml_tag_var.h
306 lines (292 loc) · 12.3 KB
/
xml_tag_var.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
#pragma once
// Copyright David Lawrence Bien 1997 - 2021.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt).
// xml_tag_var.h
// Simple XML object model based on the XML parser - variant version.
// Can't just wrap xml_tag objects because we have to store variant token objects inside the xml_tag collection.
// dbien
// 03FEB2021
#include "xml_types.h"
__XMLP_BEGIN_NAMESPACE
template < class t_TyTpTransports >
class xml_tag_var
{
typedef xml_tag_var _TyThis;
friend xml_document_var< t_TyTpTransports >;
public:
typedef t_TyTpTransports _TyTpTransports;
typedef MultiplexTuplePack_t< TGetXmlTraitsDefault, _TyTpTransports > _TyTpXmlTraits;
// The content of a tag is a series of tokens and tags.
typedef xml_token_var< _TyTpTransports > _TyXmlTokenVar;
typedef xml_read_cursor_var< _TyTpTransports > _TyReadCursorVar;
// We may contain tokens which are not shared objects, or tags which are shared for the purpose of
// using weak pointer to point to the parent xml_tag. It should allow subtrees to be stored safely
// outside of management by the xml_document container. Because we store a pointer to our parent
// xml_tag we can't share the same xml_tag in multiple place in an XML document - as might be considered
// to be nice.
// We store pointers to xml_tag object since we may be pointing to an xml_document_var object (as a parent weak pointer).
typedef unique_ptr< _TyThis > _TyUPtrThis;
typedef SharedStrongPtr< _TyUPtrThis, allocator< _TyUPtrThis >, uint32_t, false > _TyStrongThis;
typedef SharedWeakPtr< _TyUPtrThis, allocator< _TyUPtrThis >, uint32_t, false > _TyWeakThis;
typedef std::variant< _TyStrongThis, _TyXmlTokenVar > _TyVariant;
typedef std::vector< _TyVariant > _TyRgTokens;
typedef _xml_document_context_transport_var< _TyTpTransports > _TyXmlDocumentContextVar;
virtual ~xml_tag_var() = default;
xml_tag_var() = default;
xml_tag_var( const _TyStrongThis * _pspParent )
{
if ( !!_pspParent )
m_wpParent = *_pspParent;
}
xml_tag_var( xml_tag_var const & ) = default;
xml_tag_var & operator=( xml_tag_var const & ) = default;
xml_tag_var( xml_tag_var && ) = default;
xml_tag_var & operator=( xml_tag_var && _rr )
{
_TyThis acquire( std::move( _rr ) );
swap( acquire );
return *this;
}
void swap( _TyThis & _r )
{
m_opttokTag.swap( _r.m_opttokTag );
m_rgTokens.swap( _r.m_rgTokens );
m_wpParent.swap( _r.m_wpParent );
}
void AssertValid( const _TyThis * _ptagParent = nullptr ) const
{
#if ASSERTSENABLED
Assert( m_wpParent.expired() == !_ptagParent );
if ( _ptagParent )
{
_TyStrongThis spParent( _SharedWeakPtr_weak_leave_empty(), m_wpParent );
Assert( spParent->get() == _ptagParent );
}
if( !! m_opttokTag )
m_opttokTag->AssertValid();
typename _TyRgTokens::const_iterator citCur = m_rgTokens.begin();
typename _TyRgTokens::const_iterator citEnd = m_rgTokens.end();
for ( ; citEnd != citCur; ++citCur )
{
const _TyVariant & rvCur = *citCur;
if ( holds_alternative< _TyXmlTokenVar >( rvCur ) )
std::get< _TyXmlTokenVar >( rvCur ).AssertValid();
else
(*std::get< _TyStrongThis >( rvCur ))->AssertValid( this );// recurse.
}
#endif //ASSERTSENABLED
}
void AcquireTag( _TyXmlTokenVar && _rrtok )
{
m_opttokTag = std::move( _rrtok );
}
// Read from this read cursor into this object.
// REVIEW: <dbien>: Should change this to be a non-recursive implementation to avoid stack overrun vulnerability issues.
void FromXmlStream( _TyReadCursorVar & _rxrc, _TyStrongThis const & _rspThis )
{
Assert( _rxrc.FInsideDocumentTag() );
VerifyThrowSz( _rxrc.FInsideDocumentTag(), "Read cursor is in an invalid state." );
// We can't acquire the token until the call the FNextTag().
_AcquireContent( _rxrc );
// We will move down into the next tag and that is the only tag we will capture into the xml_document_var... necessarily.
if ( _rxrc.FMoveDown() )
{
bool fNextTag;
do
{
_TyStrongThis spXmlTag( std::in_place_t(), make_unique< _TyThis >( &_rspThis ) );
(*spXmlTag)->FromXmlStream( _rxrc, spXmlTag );
fNextTag = _rxrc.FNextTag( &(*spXmlTag)->m_opttokTag );
_AcquireContent( std::move( spXmlTag ) );
if ( !fNextTag )
{
_AcquireContent( _rxrc );
fNextTag = _rxrc.FMoveDown();
}
}
while ( fNextTag );
}
}
// Write this XML document to the given xml_writer<>.
// Need to pass the xml document context for the containing xml_document.
template < class t_TyXmlTransportOut >
void ToXmlStream( xml_writer< t_TyXmlTransportOut > & _rxw, _TyXmlDocumentContextVar const & _rxdcxtDocumentContext ) const
{
_rxw.SetIncludePrefixesInAttrNames( _rxdcxtDocumentContext.FIncludePrefixesInAttrNames() );
_ToXmlStream( _rxw );
}
protected:
// Add all the current content from passed context.
void _AcquireContent( _TyReadCursorVar & _rxrc )
{
_rxrc.ApplyAllContent(
// This is an xml_token<> type that is returned by a non-var xml_read_cursor.
[this]( auto * _pxtBegin, auto * _pxtEnd )
{
for ( auto * pxtCur = _pxtBegin; _pxtEnd != pxtCur; ++pxtCur )
m_rgTokens.emplace_back( std::move( *pxtCur ) );
}
);
_rxrc.ClearContent(); // The above created a bunch of empty content nodes, and they would go away naturally without ill effect, but clear them to indicate they contain nothing at all.
}
// Add _rrtag tag as content of this tag at the current end for m_rgTokens;
void _AcquireContent( _TyStrongThis && _rrspTag )
{
m_rgTokens.emplace_back( std::move( _rrspTag ) );
}
template < class t_TyXmlTransportOut >
void _ToXmlStream( xml_writer< t_TyXmlTransportOut > & _rxw ) const
{
typedef xml_writer< t_TyXmlTransportOut > _TyXmlWriter;
typedef typename _TyXmlWriter::_TyXmlWriteTag _TyXmlWriteTag;
// We must start the tag and recurse within the visit lambda:
std::visit( _VisitHelpOverloadFCall {
[this,&_rxw]( const auto & _tXmlToken ) -> void
{
_TyXmlWriteTag xwtTag( _rxw.StartTag( _tXmlToken ) );
// Just write the tag right away - the tag will be ended when the lifetime of xwtTag ends.
xwtTag.Commit();
_WriteContent( _rxw ); // recurse, potentially.
}
}, m_opttokTag->GetVariant() );
}
template < class t_TyXmlTransportOut >
void _WriteContent( xml_writer< t_TyXmlTransportOut > & _rxw ) const
{
typedef xml_writer< t_TyXmlTransportOut > _TyXmlWriter;
typename _TyRgTokens::const_iterator citCur = m_rgTokens.begin();
typename _TyRgTokens::const_iterator citEnd = m_rgTokens.end();
for ( ; citEnd != citCur; ++citCur )
{
const _TyVariant & rvCur = *citCur;
if ( holds_alternative< _TyXmlTokenVar >( rvCur ) )
{
// Just write the token to the writer - but resolve type locally:
_TyXmlTokenVar const & rxtok = std::get< _TyXmlTokenVar >( rvCur );
std::visit( _VisitHelpOverloadFCall {
[&_rxw]( const auto & _tXmlToken ) -> void
{
_rxw.WriteToken( _tXmlToken );
}
}, rxtok.GetVariant() );
}
else
{ // recurse.
(*std::get< _TyStrongThis >( rvCur ))->_ToXmlStream( _rxw );
}
}
}
typedef optional< _TyXmlTokenVar > _TyOptXmlTokenVar; // Need this because token is not default constructible.
_TyOptXmlTokenVar m_opttokTag; // The token corresponding to the tag. This is either an XMLDecl token or a tag.
_TyRgTokens m_rgTokens; // The content for this token.
_TyWeakThis m_wpParent;
};
// xml_document_var:
// This contains the root xml_tag_var as well as the namespace URI and Prefix maps and the user object.
template < class t_TyTpTransports >
class xml_document_var : public xml_tag_var< t_TyTpTransports >
{
typedef xml_document_var _TyThis;
typedef xml_tag_var< t_TyTpTransports > _TyBase;
protected:
using _TyBase::_AcquireContent;
using _TyBase::_WriteContent;
using _TyBase::m_opttokTag;
public:
using typename _TyBase::_TyUPtrThis;
using typename _TyBase::_TyWeakThis;
using typename _TyBase::_TyStrongThis;
typedef t_TyTpTransports _TyTpTransports;
typedef MultiplexTuplePack_t< TGetXmlTraitsDefault, _TyTpTransports > _TyTpXmlTraits;
typedef xml_read_cursor_var< _TyTpTransports > _TyReadCursorVar;
typedef _xml_document_context_transport_var< _TyTpTransports > _TyXmlDocumentContextVar;
xml_document_var() = default;
xml_document_var( xml_document_var const & ) = default;
xml_document_var & operator=( xml_document_var const & ) = default;
xml_document_var( xml_document_var && ) = default;
xml_document_var & operator=( xml_document_var && _rr )
{
_TyThis acquire( std::move( _rr ) );
swap( acquire );
return *this;
}
// PrCreateXmlDocument: Returns a created XML document.
static pair< _TyThis *, _TyStrongThis > PrCreateXmlDocument()
{
unique_ptr< _TyThis > upXmlDoc = make_unique< _TyThis >();
_TyThis * pXmlDoc = upXmlDoc.get();
return pair< _TyThis *, _TyStrongThis >( pXmlDoc, _TyStrongThis( std::in_place_t(), std::move( upXmlDoc ) ) );
}
void swap( _TyThis & _r )
{
if ( this == &_r )
return;
_TyBase::swap( _r );
m_varDocumentContext.swap( _r.m_varDocumentContext );
}
void AssertValid() const
{
#if ASSERTSENABLED
_TyBase::AssertValid();
#endif //ASSERTSENABLED
}
// Returns whether the XMLDecl token is a "pseudo-token" - i.e. it was created not read.
bool FPseudoXMLDecl() const
{
VerifyThrowSz( !!m_opttokTag, "Empty xml_document_var." );
return m_opttokTag->FNullValue();
}
template < class t_TyXMLDeclProperties >
void GetXMLDeclProperties( t_TyXMLDeclProperties & _rxdp ) const
{
m_varDocumentContext.GetXMLDeclProperties( _rxdp );
}
// Read from this read cursor into this object.
void FromXmlStream( _TyReadCursorVar & _rxrc, _TyStrongThis const & _rspThis )
{
Assert( _rxrc.FInProlog() || _rxrc.FInsideDocumentTag() );
VerifyThrowSz( _rxrc.FInProlog() || _rxrc.FInsideDocumentTag(), "Read cursor is in an invalid state." );
// Read each element in order. When done transfer the user object, UriMap and PrefixMap over to this object.
// We can attach at any point during the iteration and we will glean the XMLDecl top node from the read cursor.
bool fStartedInProlog = _rxrc.FInProlog();
// We only read the top-level content tokens if we are currently at the XMLDecl tag.
if ( fStartedInProlog )
_AcquireContent( _rxrc );
// We will move down into the next tag and that is the only tag we will capture into the xml_document_var... necessarily.
VerifyThrowSz( _rxrc.FMoveDown(), "No tag to copy.");
{//B
_TyStrongThis spXmlDocument;
{//B
_TyUPtrThis ptrXmlTag = make_unique< _TyBase >( &_rspThis );
spXmlDocument.emplace( std::in_place_t(), std::move( ptrXmlTag ) );
}//EB
(*spXmlDocument)->FromXmlStream( _rxrc, spXmlDocument );
bool fNextTag = _rxrc.FNextTag( &(*spXmlDocument)->m_opttokTag );
Assert( !fNextTag || !fStartedInProlog ); // If we started in the middle of an XML then we might see that there is a next tag.
_AcquireContent( std::move( spXmlDocument ) );
}//EB
// Similarly we will only acquire the ending content if we started in the prolog - otherwise it could be bogus CharData at the end of the file.
if ( fStartedInProlog )
{
Assert( _rxrc.FInEpilog() );
_AcquireContent( _rxrc );
}
// Done. Get the pseudo-tag XMLDecl from the root of the cursor.
_TyBase::AcquireTag( _rxrc.XMLDeclAcquireDocumentContext( m_varDocumentContext ) );
}
// Write this XML document to the given xml_writer<>.
template < class t_TyXmlTransportOut >
void ToXmlStream( xml_writer< t_TyXmlTransportOut > & _rxw ) const
{
_rxw.SetIncludePrefixesInAttrNames( m_varDocumentContext.FIncludePrefixesInAttrNames() );
// Just write everything to the writer.
// The writer itself writes the XMLDecl tag based on the output encoding (which it knows about), etc.
// So we use this method as a specialization to skip what would have happened in the base.
_WriteContent( _rxw ); // This recurses.
}
protected:
_TyXmlDocumentContextVar m_varDocumentContext;
};
__XMLP_END_NAMESPACE