source: soft/giet_vm/memo/include/libelfpp/dpp/xml

Last change on this file was 163, checked in by karaoui, 14 years ago

changing mover to memo
changing soft.bin to soft.elf
...

File size: 30.2 KB
RevLine 
[163]1/* -*- c++ -*-
2
3 C++ xml parser template classes
4
5 This file is part of the dpp library of C++ template classes
6
7 doc: http://diaxen.ssji.net/dpp/index.html
8 repo: https://www.ssji.net/svn/projets/trunk/libdpp
9
10 This program is free software: you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public License
12 as published by the Free Software Foundation, either version 3 of
13 the License, or (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public
21 License along with this program. If not, see
22 <http://www.gnu.org/licenses/>.
23
24 (c) 2011 Alexandre Becoulet <alexandre.becoulet@free.fr>
25
26*/
27
28#ifndef DPP_XML_HH_
29#define DPP_XML_HH_
30
31/** @file @module{XML} */
32
33#include <map>
34#include <vector>
35#include <string>
36#include <sstream>
37#include <exception>
38#include <cassert>
39#include <cctype>
40
41namespace dpp {
42
43 template <class String> class xml_scanner_ns;
44 template <class String> class xml_scanner;
45
46 /**
47 @short XML error class
48 @module {XML}
49 @header dpp/xml
50 */
51 class xml_error : public std::exception
52 {
53 std::string _what;
54
55 public:
56
57 const char* what() const throw()
58 {
59 return _what.c_str();
60 }
61
62 xml_error(const char *msg, unsigned int line)
63 {
64 std::ostringstream s;
65 s << "XML:" << msg << " at line " << line;
66 _what = s.str();
67 }
68
69 ~xml_error() throw()
70 {
71 }
72 };
73
74 //////////////////////////////////////////////////////////////////////
75 // XML scanner
76 //////////////////////////////////////////////////////////////////////
77
78 /** @internal */
79 template <class String>
80 class xml_scanner_base
81 {
82 template <class> friend class xml_scanner_ns;
83 template <class> friend class xml_scanner;
84
85 public:
86 /** @This starts XML document scanning. */
87 void scan() throw(xml_error)
88 {
89 process_root();
90 }
91
92 protected:
93 /** @internal */
94 xml_scanner_base(std::istream &i)
95 : _i(&i),
96 _depth(0),
97 _line(1)
98 {
99 }
100
101 /** @This is called on proper document end */
102 virtual void scan_end_doc()
103 {
104 }
105
106 /** @This is called on document start */
107 virtual void scan_start_doc()
108 {
109 }
110
111 /** @This is called for each XML header attribute found */
112 virtual void scan_header_value(const String &name, const String &value)
113 {
114 }
115
116 /** @This is called when a @tt CDATA section has been scand */
117 virtual void scan_cdata(const String &cdata)
118 {
119 }
120
121 /** @This is called when a comment has been scand */
122 virtual void scan_comment(const String &comment)
123 {
124 }
125
126 /** @This is called when a @tt{<! >} directive has been scand */
127 virtual void scan_directive(const String &dir)
128 {
129 }
130
131 /** @This is called when a @tt{<? ?>} instruction has been scand */
132 virtual void scan_instruction(const String &ins)
133 {
134 }
135
136 /** @This is called with content found before the next start/end tag */
137 virtual void scan_content(const String &content)
138 {
139 }
140
141 /** @This is called to substitute entity reference strings. The default
142 implementation handles predefined entities (@em amp, @em apos,
143 @em lt, @em gt and @em quot). */
144 virtual void scan_get_reference(String &ref)
145 {
146 if (ref == "amp")
147 ref = "&";
148 else if (ref == "apos")
149 ref = "'";
150 else if (ref == "gt")
151 ref = ">";
152 else if (ref == "lt")
153 ref = "<";
154 else if (ref == "quot")
155 ref = "\"";
156 else
157 ref = "";
158 }
159
160 /** @This returns current tags nesting level. */
161 unsigned int depth()
162 {
163 return _depth;
164 }
165
166 /** @This returns current line number. */
167 unsigned int line()
168 {
169 return _line;
170 }
171
172 /** @internal throw */
173 void error(const char *msg)
174 {
175 throw xml_error(msg, _line);
176 }
177
178 private:
179 int getc_eof()
180 {
181 char c;
182
183 _i->get(c);
184
185 if (_i->eof())
186 return -1;
187
188 if (!_i->good())
189 error("io error reading xml stream");
190
191 if (c == '\n')
192 _line++;
193
194 return (unsigned char)c;
195 }
196
197 int getc()
198 {
199 int c = getc_eof();
200
201 if (c < 0)
202 error("unexpected end of xml stream");
203
204 return c;
205 }
206
207 void putc(String &str, int c)
208 {
209 str += c;
210 }
211
212 void putc_data(String &str, int c)
213 {
214 switch (c)
215 {
216 case '&':
217 switch (c = getc())
218 {
219 // character reference
220 case '#': {
221 int base = 10;
222 int r = 0;
223 while ((c = getc()) != ';')
224 {
225 switch (c)
226 {
227 case 'x':
228 base = 16;
229 break;
230 case '0': case '1': case '2': case '3': case '4':
231 case '5': case '6': case '7': case '8': case '9':
232 r = r * base + c - '0';
233 break;
234 case 'A': case 'B': case 'C':
235 case 'D': case 'E': case 'F':
236 r = r * base + 10 + c - 'A';
237 break;
238 case 'a': case 'b': case 'c':
239 case 'd': case 'e': case 'f':
240 r = r * base + 10 + c - 'a';
241 break;
242 default:
243 error("bad escaping");
244 }
245 }
246 return putc(str, r);
247 }
248
249 // entity reference
250 default: {
251 String ref;
252 do {
253 ref += c;
254 c = getc();
255 } while (c != ';');
256 scan_get_reference(ref);
257 str += ref;
258 return;
259 }
260 }
261
262 default:
263 return putc(str, c);
264 }
265 }
266
267 void process_attr_value(String &value, int c)
268 {
269 for (; c != '='; c = getc())
270 if (c > 32)
271 error("unexpected char in attribute name");
272 for (c = getc(); c != '"' && c != '\''; c = getc())
273 if (c > 32)
274 error("unexpected char in attribute value");
275 int q = c;
276 for (c = getc(); c != q; c = getc())
277 putc_data(value, c);
278 }
279
280 void process_instruction()
281 {
282 _misc.clear();
283 while (1)
284 {
285 int c = getc();
286
287 switch (c)
288 {
289 case '?':
290 if (getc() != '>')
291 error("unexpected char in instruction");
292 return scan_instruction(_misc);
293
294 case '"':
295 case '\'': {
296 int q = c;
297 putc_data(_misc, c);
298 for (c = getc(); c != q; c = getc())
299 putc_data(_misc, c);
300 }
301
302 default:
303 putc(_misc, c);
304 }
305 }
306 }
307
308 void process_comment()
309 {
310 int c1 = 0, c2 = 0;
311 while (1)
312 {
313 int c = getc();
314
315 switch (c)
316 {
317 case '>':
318 if (c1 == '-' && c2 == '-')
319 {
320 _misc.resize(_misc.size()-2);
321 return scan_comment(_misc);
322 }
323
324 default:
325 c2 = c1;
326 c1 = c;
327 putc(_misc, c);
328 }
329 }
330 }
331
332 void process_cdata()
333 {
334 int c1 = 0, c2 = 0;
335 while (1)
336 {
337 int c = getc();
338
339 switch (c)
340 {
341 case '>':
342 if (c1 == ']' && c2 == ']')
343 {
344 _misc.resize(_misc.size()-2);
345 return scan_cdata(_misc);
346 }
347
348 default:
349 c2 = c1;
350 c1 = c;
351 putc(_misc, c);
352 }
353 }
354 }
355
356 void process_directive()
357 {
358 _misc.clear();
359 while (1)
360 {
361 int c = getc();
362
363 switch (c)
364 {
365 case '[':
366 for (const char *s = "CDATA["; *s; s++)
367 if (getc() != *s)
368 error("bad directive");
369 return process_cdata();
370
371 case '-':
372 if (getc() != '-')
373 error("bad directive");
374 return process_comment();
375
376 default: {
377 int r = 0;
378 if (_depth)
379 error("unexpected directive inside tag");
380 do {
381 putc(_misc, c);
382 switch (c)
383 {
384 case '"':
385 case '\'': {
386 int q = c;
387 for (c = getc(); c != q; c = getc())
388 putc_data(_misc, c);
389 putc_data(_misc, c);
390 break;
391 }
392 case '[':
393 r++;
394 break;
395 case ']':
396 r--;
397 break;
398 }
399 c = getc();
400 } while (r || c != '>');
401
402 return scan_directive(_misc);
403 }
404 }
405 }
406 }
407
408 virtual void process_assignment(int c) = 0;
409
410 bool process_attributes()
411 {
412 while (1)
413 {
414 int c = getc();
415
416 switch (c)
417 {
418 case '>':
419 return false;
420
421 case '/':
422 c = getc();
423 if (c != '>')
424 error("unexpected char in empty tag");
425 return true;
426
427 case ' ':
428 case '\t':
429 case '\r':
430 case '\n':
431 continue;
432
433 default:
434 process_assignment(c);
435 }
436 }
437 }
438
439 virtual void process_start_tag_name(int c) = 0;
440 virtual void process_end_tag_name() = 0;
441
442 void process_end_tag_tail(int c)
443 {
444 while (c != '>')
445 {
446 if (c > 32)
447 error("unexpected char in end tag");
448 c = getc();
449 }
450 }
451
452 void process_tag()
453 {
454 while (1)
455 {
456 int c = getc();
457
458 switch (c)
459 {
460 case '?':
461 return process_instruction();
462 case '!':
463 return process_directive();
464
465 case '/': {
466
467 if (!_depth--)
468 error("unexpected end tag");
469
470 process_end_tag_name();
471 return;
472 }
473
474 case ' ':
475 case '\t':
476 case '\r':
477 case '\n':
478 continue;
479
480 default:
481 return process_start_tag_name(c);
482 }
483 }
484 }
485
486 void process_root()
487 {
488 int c;
489
490 scan_start_doc();
491
492 do {
493 c = getc();
494 } while (c < 32);
495 for (const char *s = "<?xml"; *s; c = getc())
496 if (c > 32 && std::tolower(c) != *s++)
497 error("bad header");
498 while (1)
499 {
500 c = getc();
501 switch (c)
502 {
503 case '?':
504 goto hdr_done;
505
506 case ' ':
507 case '\t':
508 case '\r':
509 case '\n':
510 break;
511
512 default: {
513 String attr, value;
514
515 do {
516 putc(attr, c);
517 c = getc();
518 } while (c > 32 && c != '=');
519
520 process_attr_value(value, c);
521 scan_header_value(attr, value);
522 }
523 }
524 }
525 hdr_done:
526 if (getc() != '>')
527 error("bad header end");
528
529 c = getc_eof();
530 while (1)
531 {
532 switch (c)
533 {
534 case '<':
535 process_tag();
536 break;
537
538 case -1:
539 if (_depth)
540 error("unexpected end of xml stream");
541 scan_end_doc();
542 return;
543
544 default: {
545 String content;
546 do {
547 putc_data(content, c);
548 c = getc_eof();
549 } while (c > 0 && c != '<');
550 scan_content(content);
551 continue;
552 }
553 }
554 c = getc_eof();
555 }
556 }
557
558 private:
559 std::istream *_i;
560 unsigned int _depth;
561 unsigned int _line;
562 String _misc;
563 };
564
565 /**
566 @short XML scanner
567 @module {XML}
568 @header dpp/xml
569 @see xml_event_parser
570
571 @This implements an XML scanner. Derived classes must override
572 some functions to handle XML scanning events.
573
574 @ref xml_error exception is thrown on syntax error. Start and
575 end tags count must be balanced but tags name missmatch is not checked
576 here.
577
578 The @tt String template parameter may be used to specify a
579 string class with optional wide characters support.
580
581 @section {Example}
582 @example test/test_xml_scanner.cc:scanner P
583 @example test/test_xml_scanner.cc:use
584 @end section
585 */
586 template <class String>
587 class xml_scanner : public xml_scanner_base<String>
588 {
589 typedef xml_scanner_base<String> base;
590 std::istringstream _ss;
591
592 public:
593 /** @This create an xml scanner for processing from input stream. */
594 xml_scanner(std::istream &i)
595 : xml_scanner_base<String>(i)
596 {
597 }
598
599 /** @This create an xml scanner for processing from string content. */
600 xml_scanner(const std::string &s)
601 : _ss(s),
602 xml_scanner_base<String>(_ss)
603 {
604 }
605
606 private:
607
608 void process_assignment(int c)
609 {
610 String attr, value;
611
612 do {
613 base::putc(attr, c);
614 c = base::getc();
615 } while (c > 32 && c != '=');
616
617 base::process_attr_value(value, c);
618 scan_attribute(attr, value);
619 }
620
621 void process_start_tag_name(int c)
622 {
623 String stag;
624 do {
625 base::putc(stag, c);
626 c = base::getc();
627 } while (c > 32 && c != '>' && c != '/');
628
629 if (c == '\n')
630 base::_line--;
631 base::_i->unget();
632
633 scan_start_tag(stag);
634 if (base::process_attributes())
635 scan_end_tag(stag);
636 else
637 base::_depth++;
638 }
639
640 void process_end_tag_name()
641 {
642 String etag;
643 int c;
644
645 while ((c = base::getc()) != '>' && c > 32)
646 base::putc(etag, c);
647
648 base::process_end_tag_tail(c);
649 scan_end_tag(etag);
650 }
651
652 protected:
653
654 /** @This is called for each start tag */
655 virtual void scan_start_tag(const String &start_tag)
656 {
657 }
658
659 /** @This is called for each end tag */
660 virtual void scan_end_tag(const String &end_tag)
661 {
662 }
663
664 /** @This is called for each attribute found in the preceding start tag */
665 virtual void scan_attribute(const String &name, const String &value)
666 {
667 }
668 };
669
670 /**
671 @short XML scanner with namespace support
672 @module {XML}
673 @header dpp/xml
674 @see xml_scanner
675
676 This class implements an XML scanner. This class is similar to
677 @xref{xml_scanner} but tag and attribute namespace name is
678 extracted.
679 */
680 template <class String>
681 class xml_scanner_ns : public xml_scanner_base<String>
682 {
683 typedef xml_scanner_base<String> base;
684
685 std::istringstream _ss;
686
687 public:
688
689 /** @This create an xml scanner for processing from input stream. */
690 xml_scanner_ns(std::istream &i)
691 : xml_scanner_base<String>(i)
692 {
693 }
694
695 /** @This create an xml scanner for processing from string content. */
696 xml_scanner_ns(const std::string &s)
697 : _ss(s),
698 xml_scanner_base<String>(_ss)
699 {
700 }
701
702 private:
703 void process_assignment(int c)
704 {
705 String xmlns, attr, value;
706
707 do {
708 base::putc(xmlns, c);
709 c = base::getc();
710 } while (c > 32 && c != '=' && c != ':');
711
712 if (c == ':') {
713 c = base::getc();
714 do {
715 base::putc(attr, c);
716 c = base::getc();
717 } while (c > 32 && c != '=');
718 }
719 else
720 attr.swap(xmlns);
721
722 base::process_attr_value(value, c);
723 scan_attribute(xmlns, attr, value);
724 }
725
726 void process_start_tag_name(int c)
727 {
728 String xmlns;
729 String stag;
730 do {
731 base::putc(xmlns, c);
732 c = base::getc();
733 } while (c > 32 && c != '>' && c != '/' && c != ':');
734
735 if (c == ':') {
736 c = base::getc();
737 do {
738 base::putc(stag, c);
739 c = base::getc();
740 } while (c > 32 && c != '>' && c != '/');
741 }
742 else
743 stag.swap(xmlns);
744
745 if (c == '\n')
746 base::_line--;
747 base::_i->unget();
748
749 scan_start_tag(xmlns, stag);
750 if (base::process_attributes())
751 scan_end_tag(xmlns, stag);
752 else
753 base::_depth++;
754 }
755
756 void process_end_tag_name()
757 {
758 String xmlns;
759 String etag;
760 int c;
761
762 while (1)
763 {
764 c = base::getc();
765 if (!((c != '>' && c != ':' && c > 32)))
766 break;
767 base::putc(xmlns, c);
768 }
769
770 if (c == ':') {
771 while (1)
772 {
773 c = base::getc();
774 if (!((c != '>' && c > 32)))
775 break;
776 base::putc(etag, c);
777 }
778 }
779 else
780 etag.swap(xmlns);
781
782 base::process_end_tag_tail(c);
783 scan_end_tag(xmlns, etag);
784 }
785
786 protected:
787
788 /** @This is called for each start tag */
789 virtual void scan_start_tag(const String &xmlns, const String &start_tag)
790 {
791 }
792
793 /** @This is called for each end tag */
794 virtual void scan_end_tag(const String &xmlns, const String &end_tag)
795 {
796 }
797
798 /** @This is called for each attribute found in the preceding start tag */
799 virtual void scan_attribute(const String &xmlns, const String &name, const String &value)
800 {
801 }
802 };
803
804 /** @internal */
805 struct empty
806 {
807 };
808
809 //////////////////////////////////////////////////////////////////////
810 // XML event
811 //////////////////////////////////////////////////////////////////////
812
813 template <class String>
814 class xml_event_elements
815 {
816 protected:
817 typedef std::map<String, String> attributes_map_t;
818
819 /** @This is called for each parsed start tag.
820
821 Attributes list is passed as a non-const reference so that it
822 can be swapped with an other empty @ref std::map to avoid data copy.
823
824 @This is called once the new tag @tt Node object has
825 been pushed onto the processing stack, it can be accessed from
826 @this as well as other parsing functions by using the @ref
827 stack_top function.
828
829 @see parse_start_doc @see parse_end_tag
830 */
831 virtual void parse_start_tag(const String &name, attributes_map_t &attributes)
832 {
833 }
834
835 /** @This is called for each parsed end tag.
836
837 @This is called before the ended tag @tt Node object
838 is poped from the processing stack.
839
840 @see parse_start_tag
841 */
842 virtual void parse_end_tag(const String &name)
843 {
844 }
845
846 /** @This is called with content found before the next start/end tag */
847 virtual void parse_content(const String &content)
848 {
849 }
850
851 /** @This is called when a @tt CDATA section has been parsed */
852 virtual void parse_cdata(const String &cdata)
853 {
854 }
855 };
856
857 /**
858 @short XML event parser
859 @module {XML}
860 @header dpp/xml
861 @main
862 @see xml_scanner
863
864 @This implements an XML event based parser. A user @tt Node
865 type may be embedded in the internal processing stack to ease
866 building of in memory tree representation of XML documents (see @xref{example 2}).
867
868 The @tt String template parameter may be used to specify a
869 string class with optional wide characters support.
870
871 @ref xml_error exception is thrown on input error.
872
873 @section {Example 1}
874
875 In this example we simply reprint the XML document during parsing.
876
877 @example test/test_xml_event_parser.cc:parser P
878 @example test/test_xml_event_parser.cc:use
879 @end section
880
881 @section {Example 2}
882
883 In this example we use a @ref ref smart pointer to our @tt MyNode
884 structure to build a tree from XML input. The smart pointer
885 ensures that our tree is automatically freed even if an exception
886 is thrown during parsing.
887
888 @example test/test_xml_event_parser_tree.cc:node P
889 @example test/test_xml_event_parser_tree.cc:parser
890 @example test/test_xml_event_parser_tree.cc:use
891 @end section
892 */
893 template <class String, class Node = empty>
894 class xml_event_parser
895 : protected xml_scanner<String>
896 , public xml_event_elements<String>
897 {
898 typedef xml_scanner<String> base;
899 public:
900 typedef std::map<String, String> attributes_map_t;
901
902 /** @This forwards argument to @ref xml_scanner constructor */
903 template <typename T>
904 xml_event_parser(T &t)
905 : base(t)
906 {
907 }
908
909 /** @This starts XML parsing and returns document root @tt Node. */
910 Node parse() throw(xml_error)
911 {
912 try {
913 xml_scanner<String>::scan();
914 } catch (...) {
915 parse_error();
916 cleanup();
917 throw;
918 }
919
920 Node r = _stack.back()._tc;
921 cleanup();
922 return r;
923 }
924
925 protected:
926
927 using base::line;
928 using base::depth;
929
930 /** @This is called on XML document start.
931
932 XML header attributes list is passed as a non-const reference
933 so that it can be swaped with an other empty @ref std::map to
934 avoid data copy.
935
936 @This is called once the new tag @tt Node object has
937 been pushed onto the processing stack, it can be accessed by using
938 the @ref stack_top function.
939
940 @see parse_start_tag @see parse_end_doc
941 */
942 virtual void parse_start_doc(attributes_map_t &header_values)
943 {
944 }
945
946 /** @This is called on XML document parse end */
947 virtual void parse_end_doc()
948 {
949 }
950
951 /** @This is called when a "@tt{<! >}" directive has been parsed */
952 virtual void parse_directive(const String &dir)
953 {
954 }
955
956 /** @This is called when a comment has been parsed */
957 virtual void parse_comment(const String &comment)
958 {
959 }
960
961 /** @This is called when a "@tt{<? ?>}" instruction has been parsed */
962 virtual void parse_instruction(const String &ins)
963 {
964 }
965
966 /** @This is called when a parse error occurs, before the internal
967 stack is destroyed */
968 virtual void parse_error()
969 {
970 }
971
972 /** @This returns node on stack relative to stack top, -1 is stack top. */
973 Node & stack_top(int i)
974 {
975 return _stack[_stack.size() + i]._tc;
976 }
977
978 /** @This returns node on stack relative to stack bottom, 0 is stack bottom. */
979 Node & stack_bottom(int i)
980 {
981 return _stack[i]._tc;
982 }
983
984 /** @This returns current stack size */
985 size_t stack_size()
986 {
987 return _stack.size();
988 }
989
990 private:
991
992 /** @override */
993 void scan_cdata(const String &cdata)
994 {
995 (this->*_do_start)();
996 parse_cdata(cdata);
997 }
998
999 /** @override */
1000 void scan_comment(const String &comment)
1001 {
1002 (this->*_do_start)();
1003 parse_comment(comment);
1004 }
1005
1006 /** @override */
1007 void scan_directive(const String &dir)
1008 {
1009 (this->*_do_start)();
1010 parse_directive(dir);
1011 }
1012
1013 /** @override */
1014 void scan_instruction(const String &ins)
1015 {
1016 (this->*_do_start)();
1017 parse_instruction(ins);
1018 }
1019
1020 /** @override */
1021 void scan_content(const String &content)
1022 {
1023 (this->*_do_start)();
1024 parse_content(content);
1025 }
1026
1027 /** @override */
1028 void scan_attribute(const String &name, const String &value)
1029 {
1030 _attr[name] = value;
1031 }
1032
1033 /** @override */
1034 void scan_start_tag(const String &start_tag)
1035 {
1036 (this->*_do_start)();
1037 _attr.clear();
1038 _started_tag = start_tag;
1039 _do_start = &xml_event_parser::do_start_tag;
1040 }
1041
1042 /** @override */
1043 void scan_end_tag(const String &end_tag)
1044 {
1045 (this->*_do_start)();
1046 assert(_stack.size() > 1);
1047 if (_stack.back()._name != end_tag)
1048 base::error("end tag name doesn't match");
1049 parse_end_tag(_stack.back()._name);
1050 _stack.pop_back();
1051 }
1052
1053 /** @override */
1054 void scan_start_doc()
1055 {
1056 _do_start = &xml_event_parser::do_start_doc;
1057 }
1058
1059 /** @override */
1060 void scan_header_value(const String &name, const String &value)
1061 {
1062 _attr[name] = value;
1063 }
1064
1065 /** @override */
1066 void scan_end_doc()
1067 {
1068 assert(_stack.size() == 1);
1069 parse_end_doc();
1070 }
1071
1072 void do_start_none()
1073 {
1074 }
1075
1076 void do_start_doc()
1077 {
1078 _do_start = &xml_event_parser::do_start_none;
1079 _stack.push_back(stack_item(""));
1080 parse_start_doc(_attr);
1081 }
1082
1083 void do_start_tag()
1084 {
1085 _do_start = &xml_event_parser::do_start_none;
1086 _stack.push_back(stack_item(_started_tag));
1087 parse_start_tag(_started_tag, _attr);
1088 }
1089
1090 void cleanup()
1091 {
1092 _stack.clear();
1093 _attr.clear();
1094 _started_tag.clear();
1095 }
1096
1097 struct stack_item
1098 {
1099 stack_item(const String &name)
1100 : _name(name), _tc()
1101 {
1102 }
1103
1104 String _name;
1105 Node _tc;
1106 };
1107
1108 std::vector<stack_item> _stack;
1109 std::map<String, String> _attr;
1110 void (xml_event_parser::*_do_start)();
1111 String _started_tag;
1112 };
1113
1114 /**
1115 @short XML event parser with namespace support
1116 @module {XML}
1117 @header dpp/xml
1118 @main
1119 @see xml_event_parser
1120
1121 This class implements an XML event based parser.
1122 */
1123
1124 //////////////////////////////////////////////////////////////////////
1125 // XML dom
1126 //////////////////////////////////////////////////////////////////////
1127
1128 class xml_dom_node;
1129 inline std::ostream & operator<<(std::ostream &o, const xml_dom_node &node);
1130
1131 /**
1132 @short XML dom node base class
1133 @module {XML}
1134 @header dpp/xml
1135 */
1136 class xml_dom_node
1137 {
1138 friend std::ostream & operator<<(std::ostream &o, const xml_dom_node &node);
1139 template <class> friend class xml_dom_parser;
1140 template <class> friend class xml_dom_parent_node;
1141 template <class> friend class xml_dom_element;
1142
1143 protected:
1144 virtual ~xml_dom_node()
1145 {
1146 }
1147
1148 void push_back(xml_dom_node *b)
1149 {
1150 xml_dom_node *a = this;
1151 b->_prev = a;
1152 a->_next->_prev = b;
1153 b->_next = a->_next;
1154 a->_next = b;
1155 }
1156
1157 void push_front(xml_dom_node *b)
1158 {
1159 xml_dom_node *a = this;
1160 b->_next = a;
1161 a->_prev->_next = b;
1162 b->_prev = a->_prev;
1163 a->_prev = b;
1164 }
1165
1166 bool empty() const
1167 {
1168 return _next == _prev;
1169 }
1170
1171 private:
1172 virtual void print(std::ostream &o) const
1173 {
1174 }
1175
1176 xml_dom_node *_next;
1177 xml_dom_node *_prev;
1178 };
1179
1180 inline std::ostream & operator<<(std::ostream &o, const xml_dom_node &node)
1181 {
1182 node.print(o);
1183 return o;
1184 }
1185
1186 /** @internal */
1187 template <class String>
1188 class xml_dom_parent_node : public xml_dom_node
1189 {
1190 template <class> friend class xml_dom_parser;
1191
1192 protected:
1193 typedef xml_dom_node chld_base;
1194 typedef xml_dom_node root_base;
1195 typedef std::map<String, xml_dom_node *> child_hash_t;
1196
1197 xml_dom_parent_node()
1198 {
1199 _root._next = &_root;
1200 _root._prev = &_root;
1201 }
1202
1203 ~xml_dom_parent_node()
1204 {
1205 cleanup();
1206 }
1207
1208 void print(std::ostream &o) const
1209 {
1210 for (xml_dom_node *i = _root._next; i != &_root; i = i->_next)
1211 i->print(o);
1212 }
1213
1214 void cleanup()
1215 {
1216 xml_dom_node *j;
1217 for (xml_dom_node *i = _root._next; i != &_root; i = j)
1218 {
1219 j = i->_next;
1220 delete i;
1221 }
1222 }
1223
1224 xml_dom_node _root;
1225 child_hash_t _children;
1226 };
1227
1228 /** @internal */
1229 template <class String>
1230 class xml_dom_attr_node
1231 {
1232 typedef std::map<String, String> attr_hash_t;
1233
1234 public:
1235 /** @This returns a reference to attributes map */
1236 const attr_hash_t &get_attributes() const
1237 {
1238 return _attrs;
1239 }
1240
1241 /** @This returns a modifiable reference to attributes map */
1242 attr_hash_t &get_attributes()
1243 {
1244 return _attrs;
1245 }
1246
1247 /** @This returns value of the specified attribute or default a value */
1248 const String &get_attribute(const String &name, const String &default_ = String())
1249 {
1250 typename attr_hash_t::const_iterator i = _attrs.find(name);
1251
1252 if (i == _attrs.end())
1253 return default_;
1254
1255 return i->second;
1256 }
1257
1258 protected:
1259
1260 void print_attr(std::ostream &o) const
1261 {
1262 for (typename attr_hash_t::const_iterator i = _attrs.begin();
1263 i != _attrs.end(); i++)
1264 o << " " << i->first << "=\"" << i->second << "\"";
1265 }
1266
1267 attr_hash_t _attrs;
1268 };
1269
1270 /**
1271 @short XML dom document root node
1272 @module {XML}
1273 @header dpp/xml
1274 */
1275 template <class String>
1276 class xml_dom_doc
1277 : public xml_dom_parent_node<String>
1278 , public xml_dom_attr_node<String>
1279 {
1280 template <class> friend class xml_dom_parser;
1281 typedef xml_dom_parent_node<String> base;
1282
1283 void print(std::ostream &o) const
1284 {
1285 o << "<?xml";
1286 xml_dom_attr_node<String>::print_attr(o);
1287 o << " ?>";
1288 base::print(o);
1289 }
1290 };
1291
1292 /**
1293 @short XML dom tag element
1294 @module {XML}
1295 @header dpp/xml
1296 */
1297 template <class String>
1298 class xml_dom_element
1299 : public xml_dom_parent_node<String>
1300 , public xml_dom_attr_node<String>
1301 {
1302 template <class> friend class xml_dom_parser;
1303 typedef xml_dom_parent_node<String> base;
1304
1305 public:
1306 typedef std::map<String, String> attr_hash_t;
1307
1308 /** @This returns element name */
1309 const String &get_name() const
1310 {
1311 }
1312
1313 /** @This changes element name */
1314 void set_name(const String &name) const
1315 {
1316 _name = name;
1317 }
1318
1319 private:
1320 xml_dom_element(const String &name)
1321 : _name(name)
1322 {
1323 }
1324
1325 void print(std::ostream &o) const
1326 {
1327 o << "<" << _name;
1328 xml_dom_attr_node<String>::print_attr(o);
1329 if (base::_root.empty())
1330 o << " />";
1331 else
1332 {
1333 o << ">";
1334 base::print(o);
1335 o << "</" << _name << ">";
1336 }
1337 }
1338
1339 String _name;
1340 };
1341
1342 /**
1343 @short XML dom text and CDATA content node
1344 @module {XML}
1345 @header dpp/xml
1346 */
1347 template <class String>
1348 class xml_dom_content : public xml_dom_node
1349 {
1350 template <class> friend class xml_dom_parser;
1351
1352 public:
1353 /** @this returns content */
1354 const String & get_content() const
1355 {
1356 return _content;
1357 }
1358
1359 /** @this changes content */
1360 void set_content(const String &content) const
1361 {
1362 _content = content;
1363 }
1364
1365 /** @this returns true if content is in a CDATA section in xml file */
1366 const bool is_cdata() const
1367 {
1368 return _is_cdata;
1369 }
1370
1371 private:
1372 xml_dom_content(const String &content, bool is_cdata = false)
1373 : _content(content),
1374 _is_cdata(is_cdata)
1375 {
1376 }
1377
1378 void print(std::ostream &o) const
1379 {
1380 if (_is_cdata)
1381 o << "<![CDATA[" << _content << "]]>";
1382 else
1383 o << _content;
1384 }
1385
1386 String _content;
1387 bool _is_cdata;
1388 };
1389
1390 /**
1391 @short XML dom parser
1392 @module {XML}
1393 @header dpp/xml
1394 @see xml_event_parser
1395 @main
1396
1397 @This implements an XML dom parser. It builds an in memory
1398 tree representation of the XML input.
1399
1400 The @ref parse function must be used to start XML input parsing.
1401 Nodes can be used with @ref std::ostream objects to write XML
1402 output.
1403
1404 The @tt String template parameter may be used to specify a
1405 string class with optional wide characters support.
1406
1407 @ref xml_error exception is thrown on input error.
1408
1409 @section {Example}
1410 @example test/test_xml_dom_parser.cc:read P
1411 @end section
1412 */
1413 template <class String>
1414 class xml_dom_parser : protected xml_event_parser<String, xml_dom_parent_node<String>*>
1415 {
1416 typedef xml_event_parser<String, xml_dom_parent_node<String>*> base;
1417 typedef xml_dom_element<String> element_t;
1418 typedef xml_dom_node node_t;
1419
1420 public:
1421 typedef std::map<String, String> attributes_map_t;
1422
1423 /** @This forwards argument to @ref xml_event_parser constructor */
1424 template <typename T>
1425 xml_dom_parser(T &t)
1426 : base(t)
1427 {
1428 }
1429
1430 /** @This parses input and returns the root node */
1431 xml_dom_doc<String> * parse() throw(xml_error)
1432 {
1433 xml_dom_doc<String> *r;
1434
1435 try {
1436 r = static_cast<xml_dom_doc<String>*>(base::parse());
1437 } catch (...) {
1438 throw;
1439 }
1440
1441 return r;
1442 }
1443
1444 private:
1445
1446 /** @override */
1447 void parse_start_doc(attributes_map_t &attrs)
1448 {
1449 xml_dom_doc<String> *r = new xml_dom_doc<String>();
1450 base::stack_top(-1) = r;
1451 r->_attrs.swap(attrs);
1452 }
1453
1454 /** @override */
1455 void parse_start_tag(const String &name, attributes_map_t &attrs)
1456 {
1457 element_t *e = new xml_dom_element<String>(name);
1458 base::stack_top(-1) = e;
1459 e->_attrs.swap(attrs);
1460 }
1461
1462 /** @override */
1463 void parse_end_tag(const String &name)
1464 {
1465 base::stack_top(-2)->_root.push_front(base::stack_top(-1));
1466 }
1467
1468 /** @override */
1469 void parse_content(const String &content)
1470 {
1471 xml_dom_content<String> *c = new xml_dom_content<String>(content, false);
1472 base::stack_top(-1)->_root.push_front(c);
1473 }
1474
1475 /** @override */
1476 void parse_cdata(const String &content)
1477 {
1478 xml_dom_content<String> *c = new xml_dom_content<String>(content, true);
1479 base::stack_top(-1)->_root.push_front(c);
1480 }
1481
1482 /** @override */
1483 void parse_error()
1484 {
1485 for (size_t i = 0; i < base::stack_size(); i++)
1486 delete base::stack_bottom(i);
1487 }
1488
1489 };
1490
1491}
1492
1493#endif
1494
Note: See TracBrowser for help on using the repository browser.