Line data Source code
1 : /*************************************************************************
2 : *
3 : * Copyright (c) 2012 Kohei Yoshida
4 : *
5 : * Permission is hereby granted, free of charge, to any person
6 : * obtaining a copy of this software and associated documentation
7 : * files (the "Software"), to deal in the Software without
8 : * restriction, including without limitation the rights to use,
9 : * copy, modify, merge, publish, distribute, sublicense, and/or sell
10 : * copies of the Software, and to permit persons to whom the
11 : * Software is furnished to do so, subject to the following
12 : * conditions:
13 : *
14 : * The above copyright notice and this permission notice shall be
15 : * included in all copies or substantial portions of the Software.
16 : *
17 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 : * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 : * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 : * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 : * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 : * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 : * OTHER DEALINGS IN THE SOFTWARE.
25 : *
26 : ************************************************************************/
27 :
28 : #ifndef __ORCUS_SAX_PARSER_HPP__
29 : #define __ORCUS_SAX_PARSER_HPP__
30 :
31 : #include <exception>
32 : #include <cassert>
33 : #include <sstream>
34 :
35 : #include "pstring.hpp"
36 : #include "cell_buffer.hpp"
37 :
38 : #define ORCUS_DEBUG_SAX_PARSER 0
39 :
40 : #if ORCUS_DEBUG_SAX_PARSER
41 : #include <iostream>
42 : using std::cout;
43 : using std::endl;
44 : #endif
45 :
46 : namespace orcus {
47 :
48 : class malformed_xml_error : public std::exception
49 : {
50 : public:
51 0 : malformed_xml_error(const std::string& msg) : m_msg(msg) {}
52 0 : virtual ~malformed_xml_error() throw() {}
53 0 : virtual const char* what() const throw()
54 : {
55 0 : return m_msg.c_str();
56 : }
57 : private:
58 : std::string m_msg;
59 : };
60 :
61 : /**
62 : * Element properties passed by sax_parser to its handler's open_element()
63 : * and close_element() calls.
64 : */
65 0 : struct sax_parser_element
66 : {
67 : pstring ns; // element namespace (optional)
68 : pstring name; // element name
69 : const char* begin_pos; // position of the opening brace '<'.
70 : const char* end_pos; // position of the char after the closing brace '>'.
71 : };
72 :
73 : /**
74 : * Template-based sax parser that doesn't use function pointer for
75 : * callbacks for better performance, especially on large XML streams.
76 : */
77 : template<typename _Handler>
78 : class sax_parser
79 : {
80 : public:
81 : typedef _Handler handler_type;
82 :
83 : sax_parser(const char* content, const size_t size, handler_type& handler);
84 : ~sax_parser();
85 :
86 : void parse();
87 :
88 : private:
89 :
90 : std::string indent() const;
91 :
92 0 : void next() { ++m_pos; ++m_char; }
93 :
94 0 : void nest_up() { ++m_nest_level; }
95 0 : void nest_down()
96 : {
97 0 : assert(m_nest_level > 0);
98 0 : --m_nest_level;
99 0 : }
100 :
101 0 : inline bool has_char() const { return m_pos < m_size; }
102 :
103 0 : inline size_t remains() const
104 : {
105 : #if ORCUS_DEBUG_SAX_PARSER
106 : if (m_pos >= m_size)
107 : throw malformed_xml_error("xml stream ended prematurely.");
108 : #endif
109 0 : return m_size - m_pos;
110 : }
111 :
112 0 : char cur_char() const
113 : {
114 : #if ORCUS_DEBUG_SAX_PARSER
115 : if (m_pos >= m_size)
116 : throw malformed_xml_error("xml stream ended prematurely.");
117 : #endif
118 0 : return *m_char;
119 : }
120 :
121 : char next_char()
122 : {
123 : next();
124 : #if ORCUS_DEBUG_SAX_PARSER
125 : if (m_pos >= m_size)
126 : throw malformed_xml_error("xml stream ended prematurely.");
127 : #endif
128 0 : return *m_char;
129 : }
130 :
131 : void blank();
132 :
133 : /**
134 : * Parse XML header that occurs at the beginning of every XML stream i.e.
135 : * <?xml version="..." encoding="..." ?>
136 : */
137 : void header();
138 : void body();
139 : void element();
140 : void element_open(const char* begin_pos);
141 : void element_close(const char* begin_pos);
142 : void special_tag();
143 : void comment();
144 : void content();
145 : void characters();
146 : void characters_with_encoded_char();
147 : void attribute();
148 :
149 : void parse_encoded_char();
150 :
151 : void name(pstring& str);
152 :
153 : /**
154 : * Parse attribute value. Note that the retreived string may be stored in
155 : * the temporary cell buffer. Use the string immediately after this call
156 : * before the buffer becomes invalid.
157 : */
158 : void value(pstring& str);
159 : void value_with_encoded_char(pstring& str);
160 :
161 : static bool is_blank(char c);
162 : static bool is_alpha(char c);
163 : static bool is_name_char(char c);
164 : static bool is_numeric(char c);
165 :
166 : private:
167 : cell_buffer m_cell_buf;
168 : const char* m_content;
169 : const char* m_char;
170 : const size_t m_size;
171 : size_t m_pos;
172 : size_t m_nest_level;
173 : bool m_root_elem_open:1;
174 : handler_type& m_handler;
175 : };
176 :
177 : template<typename _Handler>
178 0 : sax_parser<_Handler>::sax_parser(
179 : const char* content, const size_t size, handler_type& handler) :
180 : m_content(content),
181 : m_char(content),
182 : m_size(size),
183 : m_pos(0),
184 : m_nest_level(0),
185 : m_root_elem_open(true),
186 0 : m_handler(handler)
187 : {
188 0 : }
189 :
190 : template<typename _Handler>
191 0 : sax_parser<_Handler>::~sax_parser()
192 : {
193 0 : }
194 :
195 : template<typename _Handler>
196 0 : void sax_parser<_Handler>::parse()
197 : {
198 0 : m_pos = 0;
199 0 : m_nest_level = 0;
200 0 : m_char = m_content;
201 0 : header();
202 0 : blank();
203 0 : body();
204 0 : }
205 :
206 : template<typename _Handler>
207 : ::std::string sax_parser<_Handler>::indent() const
208 : {
209 : ::std::ostringstream os;
210 : for (size_t i = 0; i < m_nest_level; ++i)
211 : os << " ";
212 : return os.str();
213 : }
214 :
215 : template<typename _Handler>
216 0 : void sax_parser<_Handler>::blank()
217 : {
218 : char c = cur_char();
219 0 : while (is_blank(c))
220 : c = next_char();
221 0 : }
222 :
223 : template<typename _Handler>
224 0 : void sax_parser<_Handler>::header()
225 : {
226 : char c = cur_char();
227 0 : if (c != '<' || next_char() != '?' || next_char() != 'x' || next_char() != 'm' || next_char() != 'l')
228 0 : throw malformed_xml_error("xml header must begin with '<?xml'.");
229 :
230 : next();
231 0 : blank();
232 0 : while (cur_char() != '?')
233 : {
234 0 : attribute();
235 0 : blank();
236 : }
237 0 : if (next_char() != '>')
238 0 : throw malformed_xml_error("xml header must end with '?>'.");
239 :
240 : next();
241 :
242 0 : m_handler.declaration();
243 0 : }
244 :
245 : template<typename _Handler>
246 0 : void sax_parser<_Handler>::body()
247 : {
248 0 : while (has_char())
249 : {
250 0 : if (cur_char() == '<')
251 : {
252 0 : element();
253 0 : if (!m_root_elem_open)
254 : // Root element closed. Stop parsing.
255 0 : return;
256 : }
257 : else
258 0 : characters();
259 : }
260 : }
261 :
262 : template<typename _Handler>
263 0 : void sax_parser<_Handler>::element()
264 : {
265 0 : assert(cur_char() == '<');
266 : const char* pos = m_char;
267 : char c = next_char();
268 0 : switch (c)
269 : {
270 : case '/':
271 0 : element_close(pos);
272 0 : break;
273 : case '!':
274 0 : special_tag();
275 0 : break;
276 : default:
277 0 : element_open(pos);
278 : }
279 0 : }
280 :
281 : template<typename _Handler>
282 0 : void sax_parser<_Handler>::element_open(const char* begin_pos)
283 : {
284 0 : assert(is_alpha(cur_char()));
285 :
286 : sax_parser_element elem;
287 0 : elem.begin_pos = begin_pos;
288 :
289 0 : name(elem.name);
290 0 : if (cur_char() == ':')
291 : {
292 : // this element name is namespaced.
293 : elem.ns = elem.name;
294 : next();
295 0 : name(elem.name);
296 : }
297 :
298 : while (true)
299 : {
300 0 : blank();
301 : char c = cur_char();
302 0 : if (c == '/')
303 : {
304 : // Self-closing element: <element/>
305 0 : if (next_char() != '>')
306 0 : throw malformed_xml_error("expected '/>' to self-close the element.");
307 : next();
308 0 : elem.end_pos = m_char;
309 0 : m_handler.start_element(elem);
310 0 : m_handler.end_element(elem);
311 : return;
312 : }
313 0 : else if (c == '>')
314 : {
315 : // End of opening element: <element>
316 : next();
317 0 : elem.end_pos = m_char;
318 : nest_up();
319 0 : m_handler.start_element(elem);
320 : return;
321 : }
322 : else
323 0 : attribute();
324 : }
325 : }
326 :
327 : template<typename _Handler>
328 0 : void sax_parser<_Handler>::element_close(const char* begin_pos)
329 : {
330 0 : assert(cur_char() == '/');
331 0 : nest_down();
332 : next();
333 : sax_parser_element elem;
334 0 : elem.begin_pos = begin_pos;
335 :
336 0 : name(elem.name);
337 0 : if (cur_char() == ':')
338 : {
339 : elem.ns = elem.name;
340 : next();
341 0 : name(elem.name);
342 : }
343 :
344 0 : if (cur_char() != '>')
345 0 : throw malformed_xml_error("expected '>' to close the element.");
346 : next();
347 0 : elem.end_pos = m_char;
348 :
349 0 : m_handler.end_element(elem);
350 0 : if (!m_nest_level)
351 0 : m_root_elem_open = false;
352 0 : }
353 :
354 : template<typename _Handler>
355 0 : void sax_parser<_Handler>::special_tag()
356 : {
357 0 : assert(cur_char() == '!');
358 : // This can be either <![CDATA, <!--, or <!DOCTYPE.
359 : size_t len = remains();
360 0 : if (len < 2)
361 0 : throw malformed_xml_error("special tag too short.");
362 :
363 0 : switch (next_char())
364 : {
365 : case '-':
366 : {
367 : // Possibly comment.
368 0 : if (next_char() != '-')
369 0 : throw malformed_xml_error("comment expected.");
370 :
371 : len = remains();
372 0 : if (len < 3)
373 0 : throw malformed_xml_error("malformed comment.");
374 :
375 : next();
376 0 : comment();
377 : }
378 : break;
379 : default:
380 : // TODO: Handle CDATA and DOCTYPE.
381 0 : throw malformed_xml_error("failed to parse special tag.");
382 : }
383 0 : }
384 :
385 : template<typename _Handler>
386 0 : void sax_parser<_Handler>::comment()
387 : {
388 : // Parse until we reach '-->'.
389 : size_t len = remains();
390 0 : assert(len > 3);
391 : char c = cur_char();
392 : size_t i = 0;
393 : bool hyphen = false;
394 0 : for (; i < len; ++i, c = next_char())
395 : {
396 0 : if (c == '-')
397 : {
398 0 : if (!hyphen)
399 : // first hyphen.
400 : hyphen = true;
401 : else
402 : // second hyphen.
403 : break;
404 : }
405 : else
406 : hyphen = false;
407 : }
408 :
409 0 : if (len - i < 2 || next_char() != '>')
410 0 : throw malformed_xml_error("'--' should not occur in comment other than in the closing tag.");
411 :
412 : next();
413 0 : }
414 :
415 : template<typename _Handler>
416 0 : void sax_parser<_Handler>::characters()
417 : {
418 0 : size_t first = m_pos;
419 0 : const char* p0 = m_char;
420 0 : for (; has_char(); next())
421 : {
422 0 : if (cur_char() == '<')
423 : break;
424 :
425 0 : if (cur_char() == '&')
426 : {
427 : // Text span with one or more encoded characters. Parse using cell buffer.
428 : m_cell_buf.reset();
429 0 : m_cell_buf.append(p0, m_pos-first);
430 0 : characters_with_encoded_char();
431 0 : return;
432 : }
433 : }
434 :
435 0 : if (m_pos > first)
436 : {
437 0 : size_t size = m_pos - first;
438 0 : pstring val(m_content + first, size);
439 0 : m_handler.characters(val);
440 : }
441 : }
442 :
443 : template<typename _Handler>
444 0 : void sax_parser<_Handler>::characters_with_encoded_char()
445 : {
446 0 : assert(cur_char() == '&');
447 0 : parse_encoded_char();
448 0 : assert(cur_char() != ';');
449 :
450 0 : size_t first = m_pos;
451 :
452 0 : while (has_char())
453 : {
454 0 : if (cur_char() == '&')
455 : {
456 0 : if (m_pos > first)
457 0 : m_cell_buf.append(m_content+first, m_pos-first);
458 :
459 0 : parse_encoded_char();
460 0 : assert(cur_char() != ';');
461 0 : first = m_pos;
462 : }
463 :
464 0 : if (cur_char() == '<')
465 : break;
466 :
467 0 : if (cur_char() != '&')
468 : next();
469 : }
470 :
471 0 : if (m_pos > first)
472 0 : m_cell_buf.append(m_content+first, m_pos-first);
473 :
474 0 : if (m_cell_buf.empty())
475 0 : m_handler.characters(pstring());
476 : else
477 0 : m_handler.characters(pstring(m_cell_buf.get(), m_cell_buf.size()));
478 0 : }
479 :
480 : template<typename _Handler>
481 0 : void sax_parser<_Handler>::attribute()
482 : {
483 : pstring attr_ns_name, attr_name, attr_value;
484 0 : name(attr_name);
485 0 : if (cur_char() == ':')
486 : {
487 : // Attribute name is namespaced.
488 : attr_ns_name = attr_name;
489 : next();
490 0 : name(attr_name);
491 : }
492 :
493 : #if ORCUS_DEBUG_SAX_PARSER
494 : cout << "attribute: ns='" << attr_ns_name << "', name='" << attr_name << "'" << endl;
495 : #endif
496 :
497 : char c = cur_char();
498 0 : if (c != '=')
499 : {
500 0 : std::ostringstream os;
501 0 : os << "Attribute must begin with 'name=..'. (ns='" << attr_ns_name << "', name='" << attr_name << "')";
502 0 : throw malformed_xml_error(os.str());
503 : }
504 :
505 : next();
506 0 : value(attr_value);
507 :
508 0 : m_handler.attribute(attr_ns_name, attr_name, attr_value);
509 0 : }
510 :
511 : template<typename _Handler>
512 0 : void sax_parser<_Handler>::parse_encoded_char()
513 : {
514 0 : assert(cur_char() == '&');
515 : next();
516 : const char* p0 = m_char;
517 0 : for (; has_char(); next())
518 : {
519 0 : if (cur_char() != ';')
520 0 : continue;
521 :
522 0 : size_t n = m_char - p0;
523 0 : if (!n)
524 0 : throw malformed_xml_error("empty encoded character.");
525 :
526 : #if ORCUS_DEBUG_SAX_PARSER
527 : cout << "sax_parser::parse_encoded_char: raw='" << std::string(p0, n) << "'" << endl;
528 : #endif
529 :
530 : bool found = true;
531 0 : if (n == 2)
532 : {
533 0 : if (!std::strncmp(p0, "lt", 2))
534 0 : m_cell_buf.append("<", 1);
535 0 : else if (!std::strncmp(p0, "gt", 2))
536 0 : m_cell_buf.append(">", 1);
537 : else
538 : found = false;
539 : }
540 0 : else if (n == 3)
541 : {
542 0 : if (!std::strncmp(p0, "amp", 3))
543 0 : m_cell_buf.append("&", 1);
544 : else
545 : found = false;
546 : }
547 0 : else if (n == 4)
548 : {
549 0 : if (!std::strncmp(p0, "apos", 4))
550 0 : m_cell_buf.append("'", 1);
551 0 : else if (!std::strncmp(p0, "quot", 4))
552 0 : m_cell_buf.append("\"", 1);
553 : else
554 : found = false;
555 : }
556 : else
557 : found = false;
558 :
559 : // Move to the character past ';' before returning to the parent call.
560 : next();
561 :
562 0 : if (!found)
563 : {
564 : #if ORCUS_DEBUG_SAX_PARSER
565 : cout << "sax_parser::parse_encoded_char: not a known encoding name. Use the original." << endl;
566 : #endif
567 : // Unexpected encoding name. Use the original text.
568 0 : m_cell_buf.append(p0, m_char-p0);
569 : }
570 :
571 0 : return;
572 : }
573 :
574 0 : throw malformed_xml_error("error parsing encoded character: terminating character is not found.");
575 : }
576 :
577 : template<typename _Handler>
578 0 : void sax_parser<_Handler>::name(pstring& str)
579 : {
580 0 : size_t first = m_pos;
581 : char c = cur_char();
582 0 : if (!is_alpha(c))
583 : {
584 0 : ::std::ostringstream os;
585 0 : os << "name must begin with an alphabet, but got this instead '" << c << "'";
586 0 : throw malformed_xml_error(os.str());
587 : }
588 :
589 0 : while (is_alpha(c) || is_numeric(c) || is_name_char(c))
590 : c = next_char();
591 :
592 0 : size_t size = m_pos - first;
593 0 : str = pstring(m_content+first, size);
594 0 : }
595 :
596 : template<typename _Handler>
597 0 : void sax_parser<_Handler>::value(pstring& str)
598 : {
599 : char c = cur_char();
600 0 : if (c != '"')
601 0 : throw malformed_xml_error("attribute value must be quoted");
602 :
603 : c = next_char();
604 : size_t first = m_pos;
605 : const char* p0 = m_char;
606 :
607 0 : for (; c != '"'; c = next_char())
608 : {
609 0 : if (c == '&')
610 : {
611 : // This value contains one or more encoded characters.
612 : m_cell_buf.reset();
613 0 : m_cell_buf.append(p0, m_pos-first);
614 0 : value_with_encoded_char(str);
615 0 : return;
616 : }
617 : }
618 :
619 0 : str = pstring(p0, m_pos-first);
620 :
621 : // Skip the closing quote.
622 : next();
623 : }
624 :
625 : template<typename _Handler>
626 0 : void sax_parser<_Handler>::value_with_encoded_char(pstring& str)
627 : {
628 0 : assert(cur_char() == '&');
629 0 : parse_encoded_char();
630 0 : assert(cur_char() != ';');
631 :
632 0 : size_t first = m_pos;
633 :
634 0 : while (has_char())
635 : {
636 0 : if (cur_char() == '&')
637 : {
638 0 : if (m_pos > first)
639 0 : m_cell_buf.append(m_content+first, m_pos-first);
640 :
641 0 : parse_encoded_char();
642 0 : assert(cur_char() != ';');
643 0 : first = m_pos;
644 : }
645 :
646 0 : if (cur_char() == '"')
647 : break;
648 :
649 0 : if (cur_char() != '&')
650 : next();
651 : }
652 :
653 0 : if (m_pos > first)
654 0 : m_cell_buf.append(m_content+first, m_pos-first);
655 :
656 0 : if (!m_cell_buf.empty())
657 0 : str = pstring(m_cell_buf.get(), m_cell_buf.size());
658 :
659 : // Skip the closing quote.
660 0 : assert(cur_char() == '"');
661 : next();
662 0 : }
663 :
664 : template<typename _Handler>
665 : bool sax_parser<_Handler>::is_blank(char c)
666 : {
667 0 : if (c == ' ')
668 : return true;
669 0 : if (c == 0x0A || c == 0x0D)
670 : // LF or CR
671 : return true;
672 : return false;
673 : }
674 :
675 : template<typename _Handler>
676 : bool sax_parser<_Handler>::is_alpha(char c)
677 : {
678 0 : if ('a' <= c && c <= 'z')
679 : return true;
680 0 : if ('A' <= c && c <= 'Z')
681 : return true;
682 : return false;
683 : }
684 :
685 : template<typename _Handler>
686 : bool sax_parser<_Handler>::is_name_char(char c)
687 : {
688 0 : switch (c)
689 : {
690 : case '-':
691 : case '_':
692 : return true;
693 : }
694 :
695 : return false;
696 : }
697 :
698 : template<typename _Handler>
699 : bool sax_parser<_Handler>::is_numeric(char c)
700 : {
701 0 : if ('0' <= c && c <= '9')
702 : return true;
703 : return false;
704 : }
705 :
706 : }
707 :
708 : #endif
|