| 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 |  | 
|---|
| 41 | namespace 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 |  | 
|---|