1 | /////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8 |
2 | // basic_xml_grammar.ipp: |
3 | |
4 | // (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . |
5 | // Use, modification and distribution is subject to the Boost Software |
6 | // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
7 | // http://www.boost.org/LICENSE_1_0.txt) |
8 | |
9 | // See http://www.boost.org for updates, documentation, and revision history. |
10 | |
11 | #if (defined _MSC_VER) && (_MSC_VER == 1200) |
12 | # pragma warning (disable : 4786) // too long name, harmless warning |
13 | #endif |
14 | |
15 | #include <istream> |
16 | #include <algorithm> |
17 | #include <boost/config.hpp> // typename |
18 | |
19 | #ifdef BOOST_MSVC |
20 | # pragma warning(push) |
21 | # pragma warning(disable : 4244 4511 4512) |
22 | #endif |
23 | |
24 | #include <cerrno> // errno |
25 | #include <cstring> // strerror(errno) |
26 | |
27 | // spirit stuff |
28 | #include <boost/spirit/include/classic_operators.hpp> |
29 | #include <boost/spirit/include/classic_actions.hpp> |
30 | #include <boost/spirit/include/classic_numerics.hpp> |
31 | |
32 | #ifdef BOOST_MSVC |
33 | #pragma warning(pop) |
34 | #endif |
35 | |
36 | // for head_iterator test |
37 | #include <boost/function.hpp> |
38 | |
39 | #include <boost/io/ios_state.hpp> |
40 | #include <boost/serialization/throw_exception.hpp> |
41 | #include <boost/archive/impl/basic_xml_grammar.hpp> |
42 | #include <boost/archive/xml_archive_exception.hpp> |
43 | #include <boost/archive/basic_xml_archive.hpp> |
44 | #include <boost/archive/iterators/xml_unescape.hpp> |
45 | |
46 | using namespace boost::spirit::classic; |
47 | |
48 | namespace boost { |
49 | namespace archive { |
50 | |
51 | /////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8 |
52 | // template code for basic_xml_grammar of both wchar_t and char types |
53 | |
54 | namespace xml { // anonymous |
55 | |
56 | #ifdef BOOST_MSVC |
57 | # pragma warning(push) |
58 | # pragma warning(disable : 4511 4512) |
59 | #endif |
60 | |
61 | template<class T> |
62 | struct assign_impl { |
63 | T & m_t; |
64 | void operator()(const T & rhs) const { |
65 | m_t = rhs; |
66 | } |
67 | assign_impl(T & rhs) |
68 | : m_t(rhs) |
69 | {} |
70 | }; |
71 | |
72 | template<> |
73 | struct assign_impl<std::string> { |
74 | std::string & m_t; |
75 | void operator()( |
76 | std::string::const_iterator b, |
77 | std::string::const_iterator e |
78 | ) const { |
79 | m_t.resize(n: 0); |
80 | while(b != e){ |
81 | m_t += * b; |
82 | ++b; |
83 | } |
84 | } |
85 | assign_impl(const assign_impl & rhs) |
86 | : m_t(rhs.m_t) |
87 | {} |
88 | assign_impl & operator=(assign_impl & rhs); |
89 | assign_impl(std::string & rhs) |
90 | : m_t(rhs) |
91 | {} |
92 | }; |
93 | |
94 | #ifndef BOOST_NO_STD_WSTRING |
95 | template<> |
96 | struct assign_impl<std::wstring> { |
97 | std::wstring & m_t; |
98 | void operator()( |
99 | std::wstring::const_iterator b, |
100 | std::wstring::const_iterator e |
101 | ) const { |
102 | m_t.resize(n: 0); |
103 | while(b != e){ |
104 | m_t += * b; |
105 | ++b; |
106 | } |
107 | } |
108 | assign_impl(std::wstring & rhs) |
109 | : m_t(rhs) |
110 | {} |
111 | }; |
112 | #endif |
113 | |
114 | template<class T> |
115 | assign_impl<T> assign_object(T &t){ |
116 | return assign_impl<T>(t); |
117 | } |
118 | |
119 | struct assign_level { |
120 | tracking_type & tracking_level; |
121 | void operator()(const unsigned int tracking_level_) const { |
122 | tracking_level = (0 == tracking_level_) ? false : true; |
123 | } |
124 | assign_level(tracking_type & tracking_level_) |
125 | : tracking_level(tracking_level_) |
126 | {} |
127 | }; |
128 | |
129 | template<class String, class Iterator> |
130 | struct append_string { |
131 | String & contents; |
132 | void operator()(Iterator start, Iterator end) const { |
133 | #if 0 |
134 | typedef boost::archive::iterators::xml_unescape<Iterator> translator; |
135 | contents.append( |
136 | translator(BOOST_MAKE_PFTO_WRAPPER(start)), |
137 | translator(BOOST_MAKE_PFTO_WRAPPER(end)) |
138 | ); |
139 | #endif |
140 | contents.append(start, end); |
141 | } |
142 | append_string(String & contents_) |
143 | : contents(contents_) |
144 | {} |
145 | }; |
146 | |
147 | template<class String> |
148 | struct append_char { |
149 | String & contents; |
150 | void operator()(const unsigned int char_value) const { |
151 | contents += static_cast<typename String::value_type>(char_value); |
152 | } |
153 | append_char(String & contents_) |
154 | : contents(contents_) |
155 | {} |
156 | }; |
157 | |
158 | template<class String, unsigned int c> |
159 | struct append_lit { |
160 | String & contents; |
161 | template<class X, class Y> |
162 | void operator()(const X & /*x*/, const Y & /*y*/) const { |
163 | const typename String::value_type z = c; |
164 | contents += z; |
165 | } |
166 | append_lit(String & contents_) |
167 | : contents(contents_) |
168 | {} |
169 | }; |
170 | |
171 | #ifdef BOOST_MSVC |
172 | #pragma warning(pop) |
173 | #endif |
174 | |
175 | } // namespace anonymous |
176 | |
177 | template<class CharType> |
178 | bool basic_xml_grammar<CharType>::my_parse( |
179 | typename basic_xml_grammar<CharType>::IStream & is, |
180 | const rule_t & rule_, |
181 | CharType delimiter |
182 | ) const { |
183 | if(is.fail()){ |
184 | return false; |
185 | } |
186 | |
187 | is >> std::noskipws; |
188 | |
189 | std::basic_string<CharType> arg; |
190 | |
191 | for(;;){ |
192 | CharType result; |
193 | is.get(result); |
194 | if(is.fail()){ |
195 | boost::serialization::throw_exception( |
196 | e: boost::archive::archive_exception( |
197 | archive_exception::input_stream_error, |
198 | std::strerror(errno) |
199 | ) |
200 | ); |
201 | } |
202 | if(is.eof()) |
203 | return false; |
204 | arg += result; |
205 | if(result == delimiter) |
206 | break; |
207 | } |
208 | |
209 | // read just one more character. This will be the newline after the tag |
210 | // this is so that the next operation will return fail if the archive |
211 | // is terminated. This will permit the archive to be used for debug |
212 | // and transaction data logging in the standard way. |
213 | |
214 | parse_info<typename std::basic_string<CharType>::iterator> |
215 | result = boost::spirit::classic::parse(arg.begin(), arg.end(), rule_); |
216 | return result.hit; |
217 | } |
218 | |
219 | template<class CharType> |
220 | bool basic_xml_grammar<CharType>::parse_start_tag( |
221 | typename basic_xml_grammar<CharType>::IStream & is |
222 | ){ |
223 | rv.class_name.resize(0); |
224 | return my_parse(is, rule_: STag); |
225 | } |
226 | |
227 | template<class CharType> |
228 | bool basic_xml_grammar<CharType>::parse_end_tag(IStream & is) const { |
229 | return my_parse(is, rule_: ETag); |
230 | } |
231 | |
232 | template<class CharType> |
233 | bool basic_xml_grammar<CharType>::parse_string(IStream & is, StringType & s){ |
234 | rv.contents.resize(0); |
235 | bool result = my_parse(is, rule_: content, delimiter: '<'); |
236 | // note: unget caused a problem with dinkumware. replace with |
237 | // is.unget(); |
238 | // putback another delimiter instead |
239 | is.putback('<'); |
240 | if(result) |
241 | s = rv.contents; |
242 | return result; |
243 | } |
244 | |
245 | template<class CharType> |
246 | basic_xml_grammar<CharType>::basic_xml_grammar(){ |
247 | init_chset(); |
248 | |
249 | S = |
250 | +(Sch) |
251 | ; |
252 | |
253 | // refactoring to workaround template depth on darwin |
254 | NameHead = (Letter | '_' | ':'); |
255 | NameTail = *NameChar ; |
256 | Name = |
257 | NameHead >> NameTail |
258 | ; |
259 | |
260 | Eq = |
261 | !S >> '=' >> !S |
262 | ; |
263 | |
264 | AttributeList = |
265 | *(S >> Attribute) |
266 | ; |
267 | |
268 | STag = |
269 | !S |
270 | >> '<' |
271 | >> Name [xml::assign_object(rv.object_name)] |
272 | >> AttributeList |
273 | >> !S |
274 | >> '>' |
275 | ; |
276 | |
277 | ETag = |
278 | !S |
279 | >> "</" |
280 | >> Name [xml::assign_object(rv.object_name)] |
281 | >> !S |
282 | >> '>' |
283 | ; |
284 | |
285 | // refactoring to workaround template depth on darwin |
286 | CharDataChars = +(anychar_p - chset_p(init: L"&<" )); |
287 | CharData = |
288 | CharDataChars [ |
289 | xml::append_string< |
290 | StringType, |
291 | typename std::basic_string<CharType>::const_iterator |
292 | >(rv.contents) |
293 | ] |
294 | ; |
295 | |
296 | // slight factoring works around ICE in msvc 6.0 |
297 | CharRef1 = |
298 | str_p(str: L"&#" ) >> uint_p [xml::append_char<StringType>(rv.contents)] >> L';' |
299 | ; |
300 | CharRef2 = |
301 | str_p(str: L"&#x" ) >> hex_p [xml::append_char<StringType>(rv.contents)] >> L';' |
302 | ; |
303 | CharRef = CharRef1 | CharRef2 ; |
304 | |
305 | AmpRef = str_p(str: L"&" )[xml::append_lit<StringType, L'&'>(rv.contents)]; |
306 | LTRef = str_p(str: L"<" )[xml::append_lit<StringType, L'<'>(rv.contents)]; |
307 | GTRef = str_p(str: L">" )[xml::append_lit<StringType, L'>'>(rv.contents)]; |
308 | AposRef = str_p(str: L"'" )[xml::append_lit<StringType, L'\''>(rv.contents)]; |
309 | QuoteRef = str_p(str: L""" )[xml::append_lit<StringType, L'"'>(rv.contents)]; |
310 | |
311 | Reference = |
312 | AmpRef |
313 | | LTRef |
314 | | GTRef |
315 | | AposRef |
316 | | QuoteRef |
317 | | CharRef |
318 | ; |
319 | |
320 | content = |
321 | L"<" // should be end_p |
322 | | +(Reference | CharData) >> L"<" |
323 | ; |
324 | |
325 | ClassIDAttribute = |
326 | str_p(str: BOOST_ARCHIVE_XML_CLASS_ID()) >> NameTail |
327 | >> Eq |
328 | >> L'"' |
329 | >> int_p [xml::assign_object(rv.class_id)] |
330 | >> L'"' |
331 | ; |
332 | |
333 | ObjectIDAttribute = ( |
334 | str_p(str: BOOST_ARCHIVE_XML_OBJECT_ID()) |
335 | | |
336 | str_p(str: BOOST_ARCHIVE_XML_OBJECT_REFERENCE()) |
337 | ) |
338 | >> NameTail |
339 | >> Eq |
340 | >> L'"' |
341 | >> L'_' |
342 | >> uint_p [xml::assign_object(rv.object_id)] |
343 | >> L'"' |
344 | ; |
345 | |
346 | AmpName = str_p(str: L"&" )[xml::append_lit<StringType, L'&'>(rv.class_name)]; |
347 | LTName = str_p(str: L"<" )[xml::append_lit<StringType, L'<'>(rv.class_name)]; |
348 | GTName = str_p(str: L">" )[xml::append_lit<StringType, L'>'>(rv.class_name)]; |
349 | ClassNameChar = |
350 | AmpName |
351 | | LTName |
352 | | GTName |
353 | | (anychar_p - chset_p(init: L"\"" )) [xml::append_char<StringType>(rv.class_name)] |
354 | ; |
355 | |
356 | ClassName = |
357 | * ClassNameChar |
358 | ; |
359 | |
360 | ClassNameAttribute = |
361 | str_p(str: BOOST_ARCHIVE_XML_CLASS_NAME()) |
362 | >> Eq |
363 | >> L'"' |
364 | >> ClassName |
365 | >> L'"' |
366 | ; |
367 | |
368 | TrackingAttribute = |
369 | str_p(str: BOOST_ARCHIVE_XML_TRACKING()) |
370 | >> Eq |
371 | >> L'"' |
372 | >> uint_p [xml::assign_level(rv.tracking_level)] |
373 | >> L'"' |
374 | ; |
375 | |
376 | VersionAttribute = |
377 | str_p(str: BOOST_ARCHIVE_XML_VERSION()) |
378 | >> Eq |
379 | >> L'"' |
380 | >> uint_p [xml::assign_object(rv.version)] |
381 | >> L'"' |
382 | ; |
383 | |
384 | UnusedAttribute = |
385 | Name |
386 | >> Eq |
387 | >> L'"' |
388 | >> !CharData |
389 | >> L'"' |
390 | ; |
391 | |
392 | Attribute = |
393 | ClassIDAttribute |
394 | | ObjectIDAttribute |
395 | | ClassNameAttribute |
396 | | TrackingAttribute |
397 | | VersionAttribute |
398 | | UnusedAttribute |
399 | ; |
400 | |
401 | XMLDeclChars = *(anychar_p - chset_p(init: L"?>" )); |
402 | XMLDecl = |
403 | !S |
404 | >> str_p(str: L"<?xml" ) |
405 | >> S |
406 | >> str_p(str: L"version" ) |
407 | >> Eq |
408 | >> str_p(str: L"\"1.0\"" ) |
409 | >> XMLDeclChars |
410 | >> !S |
411 | >> str_p(str: L"?>" ) |
412 | ; |
413 | |
414 | DocTypeDeclChars = *(anychar_p - chset_p(init: L">" )); |
415 | DocTypeDecl = |
416 | !S |
417 | >> str_p(str: L"<!DOCTYPE" ) |
418 | >> DocTypeDeclChars |
419 | >> L'>' |
420 | ; |
421 | |
422 | SignatureAttribute = |
423 | str_p(str: L"signature" ) |
424 | >> Eq |
425 | >> L'"' |
426 | >> Name [xml::assign_object(rv.class_name)] |
427 | >> L'"' |
428 | ; |
429 | |
430 | SerializationWrapper = |
431 | !S |
432 | >> str_p(str: L"<boost_serialization" ) |
433 | >> S |
434 | >> ( (SignatureAttribute >> S >> VersionAttribute) |
435 | | (VersionAttribute >> S >> SignatureAttribute) |
436 | ) |
437 | >> !S |
438 | >> L'>' |
439 | ; |
440 | } |
441 | |
442 | template<class CharType> |
443 | void basic_xml_grammar<CharType>::init(IStream & is){ |
444 | init_chset(); |
445 | if(! my_parse(is, rule_: XMLDecl)) |
446 | boost::serialization::throw_exception( |
447 | e: xml_archive_exception(xml_archive_exception::xml_archive_parsing_error) |
448 | ); |
449 | if(! my_parse(is, rule_: DocTypeDecl)) |
450 | boost::serialization::throw_exception( |
451 | e: xml_archive_exception(xml_archive_exception::xml_archive_parsing_error) |
452 | ); |
453 | if(! my_parse(is, rule_: SerializationWrapper)) |
454 | boost::serialization::throw_exception( |
455 | e: xml_archive_exception(xml_archive_exception::xml_archive_parsing_error) |
456 | ); |
457 | if(! std::equal(rv.class_name.begin(), rv.class_name.end(), BOOST_ARCHIVE_SIGNATURE())) |
458 | boost::serialization::throw_exception( |
459 | e: archive_exception(archive_exception::invalid_signature) |
460 | ); |
461 | } |
462 | |
463 | template<class CharType> |
464 | bool basic_xml_grammar<CharType>::windup(IStream & is) { |
465 | return my_parse(is, rule_: ETag); |
466 | } |
467 | |
468 | } // namespace archive |
469 | } // namespace boost |
470 | |