source: soft/giet_vm/mover/include/libelfpp/dpp/xml @ 160

Last change on this file since 160 was 160, checked in by karaoui, 12 years ago

giet-vm new version

File size: 30.2 KB
Line 
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.