1 #ifndef OSMIUM_IO_DETAIL_XML_INPUT_FORMAT_HPP
2 #define OSMIUM_IO_DETAIL_XML_INPUT_FORMAT_HPP
3 
4 /*
5 
6 This file is part of Osmium (https://osmcode.org/libosmium).
7 
8 Copyright 2013-2020 Jochen Topf <jochen@topf.org> and others (see README).
9 
10 Boost Software License - Version 1.0 - August 17th, 2003
11 
12 Permission is hereby granted, free of charge, to any person or organization
13 obtaining a copy of the software and accompanying documentation covered by
14 this license (the "Software") to use, reproduce, display, distribute,
15 execute, and transmit the Software, and to prepare derivative works of the
16 Software, and to permit third-parties to whom the Software is furnished to
17 do so, all subject to the following:
18 
19 The copyright notices in the Software and this entire statement, including
20 the above license grant, this restriction and the following disclaimer,
21 must be included in all copies of the Software, in whole or in part, and
22 all derivative works of the Software, unless such copies or derivative
23 works are solely in the form of machine-executable object code generated by
24 a source language processor.
25 
26 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
29 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
30 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
31 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32 DEALINGS IN THE SOFTWARE.
33 
34 */
35 
36 #include <osmium/builder/builder.hpp>
37 #include <osmium/builder/osm_object_builder.hpp>
38 #include <osmium/io/detail/input_format.hpp>
39 #include <osmium/io/detail/queue_util.hpp>
40 #include <osmium/io/error.hpp>
41 #include <osmium/io/file_format.hpp>
42 #include <osmium/io/header.hpp>
43 #include <osmium/memory/buffer.hpp>
44 #include <osmium/osm/box.hpp>
45 #include <osmium/osm/changeset.hpp>
46 #include <osmium/osm/entity_bits.hpp>
47 #include <osmium/osm/item_type.hpp>
48 #include <osmium/osm/location.hpp>
49 #include <osmium/osm/node.hpp>
50 #include <osmium/osm/node_ref.hpp>
51 #include <osmium/osm/object.hpp>
52 #include <osmium/osm/relation.hpp>
53 #include <osmium/osm/timestamp.hpp>
54 #include <osmium/osm/types.hpp>
55 #include <osmium/osm/types_from_string.hpp>
56 #include <osmium/osm/way.hpp>
57 #include <osmium/thread/util.hpp>
58 
59 #include <expat.h>
60 
61 #include <cassert>
62 #include <cstring>
63 #include <exception>
64 #include <future>
65 #include <limits>
66 #include <memory>
67 #include <string>
68 #include <utility>
69 
70 namespace osmium {
71 
72     /**
73      * Exception thrown when the XML parser failed. The exception contains
74      * (if available) information about the place where the error happened
75      * and the type of error.
76      */
77     struct xml_error : public io_error {
78 
79         uint64_t line = 0;
80         uint64_t column = 0;
81         XML_Error error_code;
82         std::string error_string;
83 
xml_errorosmium::xml_error84         explicit xml_error(const XML_Parser& parser) :
85             io_error(std::string{"XML parsing error at line "}
86                     + std::to_string(XML_GetCurrentLineNumber(parser))
87                     + ", column "
88                     + std::to_string(XML_GetCurrentColumnNumber(parser))
89                     + ": "
90                     + XML_ErrorString(XML_GetErrorCode(parser))),
91             line(XML_GetCurrentLineNumber(parser)),
92             column(XML_GetCurrentColumnNumber(parser)),
93             error_code(XML_GetErrorCode(parser)),
94             error_string(XML_ErrorString(error_code)) {
95         }
96 
xml_errorosmium::xml_error97         explicit xml_error(const std::string& message) :
98             io_error(message),
99             error_code(),
100             error_string(message) {
101         }
102 
103     }; // struct xml_error
104 
105     /**
106      * Exception thrown when an OSM XML files contains no version attribute
107      * on the 'osm' element or if the version is unknown.
108      */
109     struct format_version_error : public io_error {
110 
111         std::string version;
112 
format_version_errorosmium::format_version_error113         explicit format_version_error() :
114             io_error("Can not read file without version (missing version attribute on osm element).") {
115         }
116 
format_version_errorosmium::format_version_error117         explicit format_version_error(const char* v) :
118             io_error(std::string{"Can not read file with version "} + v),
119             version(v) {
120         }
121 
122     }; // struct format_version_error
123 
124     namespace io {
125 
126         namespace detail {
127 
128             class XMLParser final : public Parser {
129 
130                 enum {
131                     initial_buffer_size = 1024UL * 1024UL
132                 };
133 
134                 enum class context {
135                     osm,
136                     osmChange,
137                     bounds,
138                     create_section,
139                     modify_section,
140                     delete_section,
141                     node,
142                     way,
143                     relation,
144                     tag,
145                     nd,
146                     member,
147                     changeset,
148                     discussion,
149                     comment,
150                     text,
151                     obj_bbox,
152                     other
153                 }; // enum class context
154 
155                 std::vector<context> m_context_stack;
156 
157                 osmium::io::Header m_header{};
158 
159                 osmium::memory::Buffer m_buffer{initial_buffer_size,
160                                                 osmium::memory::Buffer::auto_grow::internal};
161 
162                 std::unique_ptr<osmium::builder::NodeBuilder>                m_node_builder{};
163                 std::unique_ptr<osmium::builder::WayBuilder>                 m_way_builder{};
164                 std::unique_ptr<osmium::builder::RelationBuilder>            m_relation_builder{};
165                 std::unique_ptr<osmium::builder::ChangesetBuilder>           m_changeset_builder{};
166                 std::unique_ptr<osmium::builder::ChangesetDiscussionBuilder> m_changeset_discussion_builder{};
167 
168                 std::unique_ptr<osmium::builder::TagListBuilder>             m_tl_builder{};
169                 std::unique_ptr<osmium::builder::WayNodeListBuilder>         m_wnl_builder{};
170                 std::unique_ptr<osmium::builder::RelationMemberListBuilder>  m_rml_builder{};
171 
172                 std::string m_comment_text;
173 
174                 /**
175                  * A C++ wrapper for the Expat parser that makes sure no memory
176                  * is leaked.
177                  */
178                 class ExpatXMLParser {
179 
180                     XML_Parser m_parser;
181                     std::exception_ptr m_exception_ptr{};
182 
183                     template <typename TFunc>
member_wrap(XMLParser & xml_parser,TFunc && func)184                     void member_wrap(XMLParser& xml_parser, TFunc&& func) noexcept {
185                         if (m_exception_ptr) {
186                             return;
187                         }
188                         try {
189                             std::forward<TFunc>(func)(xml_parser);
190                         } catch (...) {
191                             m_exception_ptr = std::current_exception();
192                             XML_StopParser(m_parser, 0);
193                         }
194                     }
195 
196                     template <typename TFunc>
wrap(void * data,TFunc && func)197                     static void wrap(void* data, TFunc&& func) noexcept {
198                         assert(data);
199                         auto& xml_parser = *static_cast<XMLParser*>(data);
200                         xml_parser.m_expat_xml_parser->member_wrap(xml_parser, std::forward<TFunc>(func));
201                     }
202 
start_element_wrapper(void * data,const XML_Char * element,const XML_Char ** attrs)203                     static void XMLCALL start_element_wrapper(void* data, const XML_Char* element, const XML_Char** attrs) noexcept {
204                         wrap(data, [&](XMLParser& xml_parser) {
205                             xml_parser.start_element(element, attrs);
206                         });
207                     }
208 
end_element_wrapper(void * data,const XML_Char * element)209                     static void XMLCALL end_element_wrapper(void* data, const XML_Char* element) noexcept {
210                         wrap(data, [&](XMLParser& xml_parser) {
211                             xml_parser.end_element(element);
212                         });
213                     }
214 
character_data_wrapper(void * data,const XML_Char * text,int len)215                     static void XMLCALL character_data_wrapper(void* data, const XML_Char* text, int len) noexcept {
216                         wrap(data, [&](XMLParser& xml_parser) {
217                             xml_parser.characters(text, len);
218                         });
219                     }
220 
221                     // This handler is called when there are any XML entities
222                     // declared in the OSM file. Entities are normally not used,
223                     // but they can be misused. See
224                     // https://en.wikipedia.org/wiki/Billion_laughs
225                     // The handler will just throw an error.
entity_declaration_handler(void * data,const XML_Char *,int,const XML_Char *,int,const XML_Char *,const XML_Char *,const XML_Char *,const XML_Char *)226                     static void entity_declaration_handler(void* data,
227                             const XML_Char* /*entityName*/,
228                             int /*is_parameter_entity*/,
229                             const XML_Char* /*value*/,
230                             int /*value_length*/,
231                             const XML_Char* /*base*/,
232                             const XML_Char* /*systemId*/,
233                             const XML_Char* /*publicId*/,
234                             const XML_Char* /*notationName*/) noexcept {
235                         wrap(data, [&](XMLParser& /*xml_parser*/) {
236                             throw osmium::xml_error{"XML entities are not supported"};
237                         });
238                     }
239 
240                 public:
241 
ExpatXMLParser(void * callback_object)242                     explicit ExpatXMLParser(void* callback_object) :
243                         m_parser(XML_ParserCreate(nullptr)) {
244                         if (!m_parser) {
245                             throw osmium::io_error{"Internal error: Can not create parser"};
246                         }
247                         XML_SetUserData(m_parser, callback_object);
248                         XML_SetElementHandler(m_parser, start_element_wrapper, end_element_wrapper);
249                         XML_SetCharacterDataHandler(m_parser, character_data_wrapper);
250                         XML_SetEntityDeclHandler(m_parser, entity_declaration_handler);
251                     }
252 
253                     ExpatXMLParser(const ExpatXMLParser&) = delete;
254                     ExpatXMLParser& operator=(const ExpatXMLParser&) = delete;
255 
256                     ExpatXMLParser(ExpatXMLParser&&) = delete;
257                     ExpatXMLParser& operator=(ExpatXMLParser&&) = delete;
258 
~ExpatXMLParser()259                     ~ExpatXMLParser() noexcept {
260                         XML_ParserFree(m_parser);
261                     }
262 
operator ()(const std::string & data,bool last)263                     void operator()(const std::string& data, bool last) {
264                         assert(data.size() < std::numeric_limits<int>::max());
265                         if (XML_Parse(m_parser, data.data(), static_cast<int>(data.size()), last) == XML_STATUS_ERROR) {
266                             if (m_exception_ptr) {
267                                 std::rethrow_exception(m_exception_ptr);
268                             }
269                             throw osmium::xml_error{m_parser};
270                         }
271                     }
272 
273                 }; // class ExpatXMLParser
274 
275                 ExpatXMLParser* m_expat_xml_parser{nullptr};
276 
277                 template <typename T>
check_attributes(const XML_Char ** attrs,T && check)278                 static void check_attributes(const XML_Char** attrs, T&& check) {
279                     while (*attrs) {
280                         std::forward<T>(check)(attrs[0], attrs[1]);
281                         attrs += 2;
282                     }
283                 }
284 
init_object(osmium::OSMObject & object,const XML_Char ** attrs)285                 const char* init_object(osmium::OSMObject& object, const XML_Char** attrs) {
286                     assert(m_context_stack.size() > 1);
287                     if (m_context_stack[m_context_stack.size() - 2] == context::delete_section) {
288                         object.set_visible(false);
289                     }
290 
291                     osmium::Location location;
292                     const char* user = "";
293 
294                     check_attributes(attrs, [&location, &user, &object](const XML_Char* name, const XML_Char* value) {
295                         if (!std::strcmp(name, "lon")) {
296                             location.set_lon(value);
297                         } else if (!std::strcmp(name, "lat")) {
298                             location.set_lat(value);
299                         } else if (!std::strcmp(name, "user")) {
300                             user = value;
301                         } else {
302                             object.set_attribute(name, value);
303                         }
304                     });
305 
306                     if (location && object.type() == osmium::item_type::node) {
307                         static_cast<osmium::Node&>(object).set_location(location);
308                     }
309 
310                     return user;
311                 }
312 
init_changeset(osmium::builder::ChangesetBuilder & builder,const XML_Char ** attrs)313                 static void init_changeset(osmium::builder::ChangesetBuilder& builder, const XML_Char** attrs) {
314                     osmium::Box box;
315 
316                     check_attributes(attrs, [&builder, &box](const XML_Char* name, const XML_Char* value) {
317                         if (!std::strcmp(name, "min_lon")) {
318                             box.bottom_left().set_lon(value);
319                         } else if (!std::strcmp(name, "min_lat")) {
320                             box.bottom_left().set_lat(value);
321                         } else if (!std::strcmp(name, "max_lon")) {
322                             box.top_right().set_lon(value);
323                         } else if (!std::strcmp(name, "max_lat")) {
324                             box.top_right().set_lat(value);
325                         } else if (!std::strcmp(name, "user")) {
326                             builder.set_user(value);
327                         } else {
328                             builder.set_attribute(name, value);
329                         }
330                     });
331 
332                     builder.set_bounds(box);
333                 }
334 
get_tag(osmium::builder::Builder & builder,const XML_Char ** attrs)335                 void get_tag(osmium::builder::Builder& builder, const XML_Char** attrs) {
336                     const char* k = "";
337                     const char* v = "";
338 
339                     check_attributes(attrs, [&k, &v](const XML_Char* name, const XML_Char* value) {
340                         if (name[0] == 'k' && name[1] == '\0') {
341                             k = value;
342                         } else if (name[0] == 'v' && name[1] == '\0') {
343                             v = value;
344                         }
345                     });
346 
347                     if (!m_tl_builder) {
348                         m_tl_builder.reset(new osmium::builder::TagListBuilder{builder});
349                     }
350                     m_tl_builder->add_tag(k, v);
351                 }
352 
mark_header_as_done()353                 void mark_header_as_done() {
354                     set_header_value(m_header);
355                 }
356 
top_level_element(const XML_Char * element,const XML_Char ** attrs)357                 void top_level_element(const XML_Char* element, const XML_Char** attrs) {
358                     if (!std::strcmp(element, "osm")) {
359                         m_context_stack.push_back(context::osm);
360                     } else if (!std::strcmp(element, "osmChange")){
361                         m_context_stack.push_back(context::osmChange);
362                         m_header.set_has_multiple_object_versions(true);
363                     } else {
364                         throw osmium::xml_error{std::string{"Unknown top-level element: "} + element};
365                     }
366 
367                     check_attributes(attrs, [this](const XML_Char* name, const XML_Char* value) {
368                         if (!std::strcmp(name, "version")) {
369                             m_header.set("version", value);
370                             if (std::strcmp(value, "0.6") != 0) {
371                                 throw osmium::format_version_error{value};
372                             }
373                         } else if (!std::strcmp(name, "generator")) {
374                             m_header.set("generator", value);
375                         } else if (!std::strcmp(name, "upload")) {
376                             m_header.set("xml_josm_upload", value);
377                         }
378                         // ignore other attributes
379                     });
380 
381                     if (m_header.get("version").empty()) {
382                         throw osmium::format_version_error{};
383                     }
384                 }
385 
data_level_element(const XML_Char * element,const XML_Char ** attrs,bool in_change_section)386                 void data_level_element(const XML_Char* element, const XML_Char** attrs, bool in_change_section) {
387                     assert(!m_node_builder);
388                     assert(!m_way_builder);
389                     assert(!m_relation_builder);
390                     assert(!m_changeset_builder);
391                     assert(!m_changeset_discussion_builder);
392                     assert(!m_tl_builder);
393                     assert(!m_wnl_builder);
394                     assert(!m_rml_builder);
395 
396                     if (!std::strcmp(element, "node")) {
397                         m_context_stack.push_back(context::node);
398                         mark_header_as_done();
399                         if (read_types() & osmium::osm_entity_bits::node) {
400                             m_node_builder.reset(new osmium::builder::NodeBuilder{m_buffer});
401                             m_node_builder->set_user(init_object(m_node_builder->object(), attrs));
402                         }
403                         return;
404                     }
405 
406                     if (!std::strcmp(element, "way")) {
407                         m_context_stack.push_back(context::way);
408                         mark_header_as_done();
409                         if (read_types() & osmium::osm_entity_bits::way) {
410                             m_way_builder.reset(new osmium::builder::WayBuilder{m_buffer});
411                             m_way_builder->set_user(init_object(m_way_builder->object(), attrs));
412                         }
413                         return;
414                     }
415 
416                     if (!std::strcmp(element, "relation")) {
417                         m_context_stack.push_back(context::relation);
418                         mark_header_as_done();
419                         if (read_types() & osmium::osm_entity_bits::relation) {
420                             m_relation_builder.reset(new osmium::builder::RelationBuilder{m_buffer});
421                             m_relation_builder->set_user(init_object(m_relation_builder->object(), attrs));
422                         }
423                         return;
424                     }
425 
426                     if (in_change_section) {
427                         throw xml_error{"create/modify/delete sections can only contain nodes, ways, and relations"};
428                     }
429 
430                     if (!std::strcmp(element, "changeset")) {
431                         m_context_stack.push_back(context::changeset);
432                         mark_header_as_done();
433                         if (read_types() & osmium::osm_entity_bits::changeset) {
434                             m_changeset_builder.reset(new osmium::builder::ChangesetBuilder{m_buffer});
435                             init_changeset(*m_changeset_builder, attrs);
436                         }
437                     } else if (!std::strcmp(element, "create")) {
438                         if (m_context_stack.back() != context::osmChange) {
439                             throw xml_error{"<create> only allowed in OSM change files"};
440                         }
441                         m_context_stack.push_back(context::create_section);
442                         mark_header_as_done();
443                     } else if (!std::strcmp(element, "modify")) {
444                         if (m_context_stack.back() != context::osmChange) {
445                             throw xml_error{"<modify> only allowed in OSM change files"};
446                         }
447                         m_context_stack.push_back(context::modify_section);
448                         mark_header_as_done();
449                     } else if (!std::strcmp(element, "delete")) {
450                         if (m_context_stack.back() != context::osmChange) {
451                             throw xml_error{"<delete> only allowed in OSM change files"};
452                         }
453                         m_context_stack.push_back(context::delete_section);
454                         mark_header_as_done();
455                     } else if (!std::strcmp(element, "bounds")) {
456                         m_context_stack.push_back(context::bounds);
457                         osmium::Location min;
458                         osmium::Location max;
459                         check_attributes(attrs, [&min, &max](const XML_Char* name, const XML_Char* value) {
460                             if (!std::strcmp(name, "minlon")) {
461                                 min.set_lon(value);
462                             } else if (!std::strcmp(name, "minlat")) {
463                                 min.set_lat(value);
464                             } else if (!std::strcmp(name, "maxlon")) {
465                                 max.set_lon(value);
466                             } else if (!std::strcmp(name, "maxlat")) {
467                                 max.set_lat(value);
468                             }
469                         });
470                         osmium::Box box;
471                         box.extend(min).extend(max);
472                         m_header.add_box(box);
473                     } else {
474                         m_context_stack.push_back(context::other);
475                     }
476                 }
477 
start_element(const XML_Char * element,const XML_Char ** attrs)478                 void start_element(const XML_Char* element, const XML_Char** attrs) {
479                     if (m_context_stack.empty()) {
480                         top_level_element(element, attrs);
481                         return;
482                     }
483 
484                     switch (m_context_stack.back()) {
485                         case context::osm:
486                             // fallthrough
487                         case context::osmChange:
488                             data_level_element(element, attrs, false);
489                             break;
490                         case context::create_section:
491                             // fallthrough
492                         case context::modify_section:
493                             // fallthrough
494                         case context::delete_section:
495                             data_level_element(element, attrs, true);
496                             break;
497                         case context::node:
498                             if (!std::strcmp(element, "tag")) {
499                                 m_context_stack.push_back(context::tag);
500                                 if (read_types() & osmium::osm_entity_bits::node) {
501                                     get_tag(*m_node_builder, attrs);
502                                 }
503                             } else {
504                                 throw xml_error{std::string{"Unknown element in <node>: "} + element};
505                             }
506                             break;
507                         case context::way:
508                             if (!std::strcmp(element, "nd")) {
509                                 m_context_stack.push_back(context::nd);
510                                 if (read_types() & osmium::osm_entity_bits::way) {
511                                     m_tl_builder.reset();
512 
513                                     if (!m_wnl_builder) {
514                                         m_wnl_builder.reset(new osmium::builder::WayNodeListBuilder{*m_way_builder});
515                                     }
516 
517                                     NodeRef nr;
518                                     check_attributes(attrs, [&nr](const XML_Char* name, const XML_Char* value) {
519                                         if (!std::strcmp(name, "ref")) {
520                                             nr.set_ref(osmium::string_to_object_id(value));
521                                         } else if (!std::strcmp(name, "lon")) {
522                                             nr.location().set_lon(value);
523                                         } else if (!std::strcmp(name, "lat")) {
524                                             nr.location().set_lat(value);
525                                         }
526                                     });
527                                     m_wnl_builder->add_node_ref(nr);
528                                 }
529                             } else if (!std::strcmp(element, "tag")) {
530                                 m_context_stack.push_back(context::tag);
531                                 if (read_types() & osmium::osm_entity_bits::way) {
532                                     m_wnl_builder.reset();
533                                     get_tag(*m_way_builder, attrs);
534                                 }
535                             } else if (!std::strcmp(element, "bbox") || !std::strcmp(element, "bounds")) {
536                                 m_context_stack.push_back(context::obj_bbox);
537                             } else {
538                                 throw xml_error{std::string{"Unknown element in <way>: "} + element};
539                             }
540                             break;
541                         case context::relation:
542                             if (!std::strcmp(element, "member")) {
543                                 m_context_stack.push_back(context::member);
544                                 if (read_types() & osmium::osm_entity_bits::relation) {
545                                     m_tl_builder.reset();
546 
547                                     if (!m_rml_builder) {
548                                         m_rml_builder.reset(new osmium::builder::RelationMemberListBuilder{*m_relation_builder});
549                                     }
550 
551                                     item_type type = item_type::undefined;
552                                     object_id_type ref = 0;
553                                     bool ref_is_set = false;
554                                     const char* role = "";
555                                     check_attributes(attrs, [&type, &ref, &ref_is_set, &role](const XML_Char* name, const XML_Char* value) {
556                                         if (!std::strcmp(name, "type")) {
557                                             type = char_to_item_type(value[0]);
558                                         } else if (!std::strcmp(name, "ref")) {
559                                             ref = osmium::string_to_object_id(value);
560                                             ref_is_set = true;
561                                         } else if (!std::strcmp(name, "role")) {
562                                             role = static_cast<const char*>(value);
563                                         }
564                                     });
565                                     if (type != item_type::node && type != item_type::way && type != item_type::relation) {
566                                         throw osmium::xml_error{"Unknown type on relation <member>"};
567                                     }
568                                     if (!ref_is_set) {
569                                         throw osmium::xml_error{"Missing ref on relation <member>"};
570                                     }
571                                     m_rml_builder->add_member(type, ref, role);
572                                 }
573                             } else if (!std::strcmp(element, "tag")) {
574                                 m_context_stack.push_back(context::tag);
575                                 if (read_types() & osmium::osm_entity_bits::relation) {
576                                     m_rml_builder.reset();
577                                     get_tag(*m_relation_builder, attrs);
578                                 }
579                             } else if (!std::strcmp(element, "bbox") || !std::strcmp(element, "bounds")) {
580                                 m_context_stack.push_back(context::obj_bbox);
581                             } else {
582                                 throw xml_error{std::string{"Unknown element in <relation>: "} + element};
583                             }
584                             break;
585                         case context::tag:
586                             throw xml_error{"No element inside <tag> allowed"};
587                         case context::nd:
588                             throw xml_error{"No element inside <nd> allowed"};
589                         case context::member:
590                             throw xml_error{"No element inside <member> allowed"};
591                         case context::changeset:
592                             if (!std::strcmp(element, "discussion")) {
593                                 m_context_stack.push_back(context::discussion);
594                                 if (read_types() & osmium::osm_entity_bits::changeset) {
595                                     m_tl_builder.reset();
596                                     if (!m_changeset_discussion_builder) {
597                                         m_changeset_discussion_builder.reset(new osmium::builder::ChangesetDiscussionBuilder{*m_changeset_builder});
598                                     }
599                                 }
600                             } else if (!std::strcmp(element, "tag")) {
601                                 m_context_stack.push_back(context::tag);
602                                 if (read_types() & osmium::osm_entity_bits::changeset) {
603                                     m_changeset_discussion_builder.reset();
604                                     get_tag(*m_changeset_builder, attrs);
605                                 }
606                             } else {
607                                 throw xml_error{std::string{"Unknown element in <changeset>: "} + element};
608                             }
609                             break;
610                         case context::discussion:
611                             if (!std::strcmp(element, "comment")) {
612                                 m_context_stack.push_back(context::comment);
613                                 if (read_types() & osmium::osm_entity_bits::changeset) {
614                                     osmium::Timestamp date;
615                                     osmium::user_id_type uid = 0;
616                                     const char* user = "";
617                                     check_attributes(attrs, [&date, &uid, &user](const XML_Char* name, const XML_Char* value) {
618                                         if (!std::strcmp(name, "date")) {
619                                             date = osmium::Timestamp{value};
620                                         } else if (!std::strcmp(name, "uid")) {
621                                             uid = osmium::string_to_uid(value);
622                                         } else if (!std::strcmp(name, "user")) {
623                                             user = static_cast<const char*>(value);
624                                         }
625                                     });
626                                     m_changeset_discussion_builder->add_comment(date, uid, user);
627                                 }
628                             } else {
629                                 throw xml_error{std::string{"Unknown element in <discussion>: "} + element};
630                             }
631                             break;
632                         case context::comment:
633                             if (!std::strcmp(element, "text")) {
634                                 m_context_stack.push_back(context::text);
635                             } else {
636                                 throw xml_error{std::string{"Unknown element in <comment>: "} + element};
637                             }
638                             break;
639                         case context::text:
640                             throw osmium::xml_error{"No element in <text> allowed"};
641                         case context::bounds:
642                             throw osmium::xml_error{"No element in <bounds> allowed"};
643                         case context::obj_bbox:
644                             throw osmium::xml_error{"No element in <bbox>/<bounds> allowed"};
645                         case context::other:
646                             throw xml_error{"xml file nested too deep"};
647                     }
648                 }
649 
650 #ifdef NDEBUG
end_element(const XML_Char *)651                 void end_element(const XML_Char* /*element*/) {
652 #else
653                 void end_element(const XML_Char* element) {
654 #endif
655                     assert(!m_context_stack.empty());
656                     switch (m_context_stack.back()) {
657                         case context::osm:
658                             assert(!std::strcmp(element, "osm"));
659                             mark_header_as_done();
660                             break;
661                         case context::osmChange:
662                             assert(!std::strcmp(element, "osmChange"));
663                             mark_header_as_done();
664                             break;
665                         case context::create_section:
666                             assert(!std::strcmp(element, "create"));
667                             break;
668                         case context::modify_section:
669                             assert(!std::strcmp(element, "modify"));
670                             break;
671                         case context::delete_section:
672                             assert(!std::strcmp(element, "delete"));
673                             break;
674                         case context::node:
675                             assert(!std::strcmp(element, "node"));
676                             if (read_types() & osmium::osm_entity_bits::node) {
677                                 m_tl_builder.reset();
678                                 m_node_builder.reset();
679                                 m_buffer.commit();
680                                 flush_buffer();
681                             }
682                             break;
683                         case context::way:
684                             assert(!std::strcmp(element, "way"));
685                             if (read_types() & osmium::osm_entity_bits::way) {
686                                 m_tl_builder.reset();
687                                 m_wnl_builder.reset();
688                                 m_way_builder.reset();
689                                 m_buffer.commit();
690                                 flush_buffer();
691                             }
692                             break;
693                         case context::relation:
694                             assert(!std::strcmp(element, "relation"));
695                             if (read_types() & osmium::osm_entity_bits::relation) {
696                                 m_tl_builder.reset();
697                                 m_rml_builder.reset();
698                                 m_relation_builder.reset();
699                                 m_buffer.commit();
700                                 flush_buffer();
701                             }
702                             break;
703                         case context::tag:
704                             break;
705                         case context::nd:
706                             break;
707                         case context::member:
708                             break;
709                         case context::changeset:
710                             assert(!std::strcmp(element, "changeset"));
711                             if (read_types() & osmium::osm_entity_bits::changeset) {
712                                 m_tl_builder.reset();
713                                 m_changeset_discussion_builder.reset();
714                                 m_changeset_builder.reset();
715                                 m_buffer.commit();
716                                 flush_buffer();
717                             }
718                             break;
719                         case context::discussion:
720                             assert(!std::strcmp(element, "discussion"));
721                             break;
722                         case context::comment:
723                             assert(!std::strcmp(element, "comment"));
724                             break;
725                         case context::text:
726                             assert(!std::strcmp(element, "text"));
727                             if (read_types() & osmium::osm_entity_bits::changeset) {
728                                 m_changeset_discussion_builder->add_comment_text(m_comment_text);
729                                 m_comment_text.clear();
730                             }
731                             break;
732                         case context::bounds:
733                             assert(!std::strcmp(element, "bounds"));
734                             break;
735                         case context::obj_bbox:
736                             assert(!std::strcmp(element, "bbox") || !std::strcmp(element, "bounds"));
737                             break;
738                         case context::other:
739                             break;
740                     }
741                     m_context_stack.pop_back();
742                 }
743 
744                 void characters(const XML_Char* text, int len) {
745                     if ((read_types() & osmium::osm_entity_bits::changeset) &&
746                         !m_context_stack.empty() &&
747                         m_context_stack.back() == context::text) {
748                         m_comment_text.append(text, len);
749                     }
750                 }
751 
752                 void flush_buffer() {
753                     if (m_buffer.has_nested_buffers()) {
754                         std::unique_ptr<osmium::memory::Buffer> buffer_ptr{m_buffer.get_last_nested()};
755                         send_to_output_queue(std::move(*buffer_ptr));
756                     }
757                 }
758 
759             public:
760 
761                 explicit XMLParser(parser_arguments& args) :
762                     Parser(args) {
763                 }
764 
765                 XMLParser(const XMLParser&) = delete;
766                 XMLParser& operator=(const XMLParser&) = delete;
767 
768                 XMLParser(XMLParser&&) = delete;
769                 XMLParser& operator=(XMLParser&&) = delete;
770 
771                 ~XMLParser() noexcept = default;
772 
773                 void run() override {
774                     osmium::thread::set_thread_name("_osmium_xml_in");
775 
776                     ExpatXMLParser parser{this};
777                     m_expat_xml_parser = &parser;
778 
779                     while (!input_done()) {
780                         const std::string data{get_input()};
781                         parser(data, input_done());
782                         if (read_types() == osmium::osm_entity_bits::nothing && header_is_done()) {
783                             break;
784                         }
785                     }
786 
787                     mark_header_as_done();
788 
789                     if (m_buffer.committed() > 0) {
790                         send_to_output_queue(std::move(m_buffer));
791                     }
792                 }
793 
794             }; // class XMLParser
795 
796             // we want the register_parser() function to run, setting
797             // the variable is only a side-effect, it will never be used
798             const bool registered_xml_parser = ParserFactory::instance().register_parser(
799                 file_format::xml,
__anon801d38ba0e02(parser_arguments& args) 800                 [](parser_arguments& args) {
801                     return std::unique_ptr<Parser>(new XMLParser{args});
802             });
803 
804             // dummy function to silence the unused variable warning from above
get_registered_xml_parser()805             inline bool get_registered_xml_parser() noexcept {
806                 return registered_xml_parser;
807             }
808 
809         } // namespace detail
810 
811     } // namespace io
812 
813 } // namespace osmium
814 
815 #endif // OSMIUM_IO_DETAIL_XML_INPUT_FORMAT_HPP
816