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