1 #ifndef OSMIUM_IO_DETAIL_OPL_PARSER_FUNCTIONS_HPP 2 #define OSMIUM_IO_DETAIL_OPL_PARSER_FUNCTIONS_HPP 3 4 /* 5 6 This file is part of Osmium (https://osmcode.org/libosmium). 7 8 Copyright 2013-2021 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/osm_object_builder.hpp> 37 #include <osmium/io/detail/string_util.hpp> 38 #include <osmium/io/error.hpp> 39 #include <osmium/memory/buffer.hpp> 40 #include <osmium/osm/box.hpp> 41 #include <osmium/osm/changeset.hpp> 42 #include <osmium/osm/entity_bits.hpp> 43 #include <osmium/osm/item_type.hpp> 44 #include <osmium/osm/location.hpp> 45 #include <osmium/osm/node.hpp> 46 #include <osmium/osm/relation.hpp> 47 #include <osmium/osm/timestamp.hpp> 48 #include <osmium/osm/types.hpp> 49 #include <osmium/osm/way.hpp> 50 51 #include <cstdint> 52 #include <cstdlib> 53 #include <iterator> 54 #include <limits> 55 #include <stdexcept> 56 #include <string> 57 58 namespace osmium { 59 60 namespace builder { 61 class Builder; 62 } // namespace builder 63 64 /** 65 * Exception thrown when there was a problem with parsing the OPL format 66 * of a file. 67 */ 68 struct opl_error : public io_error { 69 70 uint64_t line = 0; 71 uint64_t column = 0; 72 const char* data; 73 std::string msg; 74 opl_errorosmium::opl_error75 explicit opl_error(const std::string& what, const char* d = nullptr) : 76 io_error(std::string("OPL error: ") + what), 77 data(d), 78 msg("OPL error: ") { 79 msg.append(what); 80 } 81 opl_errorosmium::opl_error82 explicit opl_error(const char* what, const char* d = nullptr) : 83 io_error(std::string{"OPL error: "} + what), 84 data(d), 85 msg("OPL error: ") { 86 msg.append(what); 87 } 88 set_pososmium::opl_error89 void set_pos(uint64_t l, uint64_t col) { 90 line = l; 91 column = col; 92 msg.append(" on line "); 93 msg.append(std::to_string(line)); 94 msg.append(" column "); 95 msg.append(std::to_string(column)); 96 } 97 whatosmium::opl_error98 const char* what() const noexcept override { 99 return msg.c_str(); 100 } 101 102 }; // struct opl_error 103 104 namespace io { 105 106 namespace detail { 107 108 /** 109 * Consume consecutive space and tab characters. There must be 110 * at least one. 111 */ opl_parse_space(const char ** s)112 inline void opl_parse_space(const char** s) { 113 if (**s != ' ' && **s != '\t') { 114 throw opl_error{"expected space or tab character", *s}; 115 } 116 do { 117 ++*s; 118 } while (**s == ' ' || **s == '\t'); 119 } 120 121 /** 122 * Check whether s points to something else than the end of the 123 * string or a space or tab. 124 */ opl_non_empty(const char * s)125 inline bool opl_non_empty(const char *s) { 126 return *s != '\0' && *s != ' ' && *s != '\t'; 127 } 128 129 /** 130 * Skip to the next space or tab character or the end of the 131 * string. 132 */ opl_skip_section(const char ** s)133 inline const char* opl_skip_section(const char** s) noexcept { 134 while (opl_non_empty(*s)) { 135 ++*s; 136 } 137 return *s; 138 } 139 140 /** 141 * Parse OPL-escaped strings with hex code with a '%' at the end. 142 * Appends resulting unicode character to the result string. 143 * 144 * Returns a pointer to next character that needs to be consumed. 145 */ opl_parse_escaped(const char ** data,std::string & result)146 inline void opl_parse_escaped(const char** data, std::string& result) { 147 assert(data); 148 assert(*data); 149 const char* s = *data; 150 uint32_t value = 0; 151 const int max_length = sizeof(value) * 2 /* hex chars per byte */; 152 int length = 0; 153 while (++length <= max_length) { 154 if (*s == '\0') { 155 throw opl_error{"eol", s}; 156 } 157 if (*s == '%') { 158 ++s; 159 if (value == 0) { 160 result += '%'; 161 } else { 162 append_codepoint_as_utf8(value, std::back_inserter(result)); 163 } 164 *data = s; 165 return; 166 } 167 value <<= 4U; 168 if (*s >= '0' && *s <= '9') { 169 value += *s - '0'; 170 } else if (*s >= 'a' && *s <= 'f') { 171 value += *s - 'a' + 10; 172 } else if (*s >= 'A' && *s <= 'F') { 173 value += *s - 'A' + 10; 174 } else { 175 throw opl_error{"not a hex char", s}; 176 } 177 ++s; 178 } 179 throw opl_error{"hex escape too long", s}; 180 } 181 182 /** 183 * Parse a string up to end of string or next space, tab, comma, or 184 * equal sign. 185 * 186 * Appends characters to the result string. 187 * 188 * Returns a pointer to next character that needs to be consumed. 189 */ opl_parse_string(const char ** data,std::string & result)190 inline void opl_parse_string(const char** data, std::string& result) { 191 assert(data); 192 assert(*data); 193 const char* s = *data; 194 while (true) { 195 if (*s == '\0' || *s == ' ' || *s == '\t' || *s == ',' || *s == '=') { 196 break; 197 } 198 if (*s == '%') { 199 ++s; 200 opl_parse_escaped(&s, result); 201 } else { 202 result += *s; 203 ++s; 204 } 205 } 206 *data = s; 207 } 208 209 // Arbitrary limit how long integers can get 210 enum { 211 max_int_len = 16 212 }; 213 214 template <typename T> opl_parse_int(const char ** s)215 inline T opl_parse_int(const char** s) { 216 if (**s == '\0') { 217 throw opl_error{"expected integer", *s}; 218 } 219 const bool negative = (**s == '-'); 220 if (negative) { 221 ++*s; 222 } 223 224 int64_t value = 0; 225 226 int n = max_int_len; 227 while (**s >= '0' && **s <= '9') { 228 if (--n == 0) { 229 throw opl_error{"integer too long", *s}; 230 } 231 value *= 10; 232 value += **s - '0'; 233 ++*s; 234 } 235 236 if (n == max_int_len) { 237 throw opl_error{"expected integer", *s}; 238 } 239 240 if (negative) { 241 value = -value; 242 if (value < std::numeric_limits<T>::min()) { 243 throw opl_error{"integer too long", *s}; 244 } 245 } else { 246 if (value > std::numeric_limits<T>::max()) { 247 throw opl_error{"integer too long", *s}; 248 } 249 } 250 251 return T(value); 252 } 253 opl_parse_id(const char ** s)254 inline osmium::object_id_type opl_parse_id(const char** s) { 255 return opl_parse_int<osmium::object_id_type>(s); 256 } 257 opl_parse_changeset_id(const char ** s)258 inline osmium::changeset_id_type opl_parse_changeset_id(const char** s) { 259 return opl_parse_int<osmium::changeset_id_type>(s); 260 } 261 opl_parse_version(const char ** s)262 inline osmium::object_version_type opl_parse_version(const char** s) { 263 return opl_parse_int<osmium::object_version_type>(s); 264 } 265 opl_parse_visible(const char ** data)266 inline bool opl_parse_visible(const char** data) { 267 if (**data == 'V') { 268 ++*data; 269 return true; 270 } 271 if (**data == 'D') { 272 ++*data; 273 return false; 274 } 275 throw opl_error{"invalid visible flag", *data}; 276 } 277 opl_parse_uid(const char ** s)278 inline osmium::user_id_type opl_parse_uid(const char** s) { 279 return opl_parse_int<osmium::user_id_type>(s); 280 } 281 opl_parse_timestamp(const char ** s)282 inline osmium::Timestamp opl_parse_timestamp(const char** s) { 283 try { 284 if (**s == '\0' || **s == ' ' || **s == '\t') { 285 return osmium::Timestamp{}; 286 } 287 osmium::Timestamp timestamp{*s}; 288 *s += 20; 289 return timestamp; 290 } catch (const std::invalid_argument&) { 291 throw opl_error{"can not parse timestamp", *s}; 292 } 293 } 294 295 /** 296 * Check if data points to given character and consume it. 297 * Throw error otherwise. 298 */ opl_parse_char(const char ** data,char c)299 inline void opl_parse_char(const char** data, char c) { 300 if (**data == c) { 301 ++*data; 302 return; 303 } 304 std::string msg{"expected '"}; 305 msg += c; 306 msg += "'"; 307 throw opl_error{msg, *data}; 308 } 309 310 /** 311 * Parse a list of tags in the format 'key=value,key=value,...' 312 * 313 * Tags will be added to the buffer using a TagListBuilder. 314 */ opl_parse_tags(const char * s,osmium::memory::Buffer & buffer,osmium::builder::Builder * parent_builder=nullptr)315 inline void opl_parse_tags(const char* s, osmium::memory::Buffer& buffer, osmium::builder::Builder* parent_builder = nullptr) { 316 osmium::builder::TagListBuilder builder{buffer, parent_builder}; 317 std::string key; 318 std::string value; 319 while (true) { 320 opl_parse_string(&s, key); 321 opl_parse_char(&s, '='); 322 opl_parse_string(&s, value); 323 builder.add_tag(key, value); 324 if (*s == ' ' || *s == '\t' || *s == '\0') { 325 break; 326 } 327 opl_parse_char(&s, ','); 328 key.clear(); 329 value.clear(); 330 } 331 } 332 333 /** 334 * Parse a number of nodes in the format "nID,nID,nID..." 335 * 336 * Nodes will be added to the buffer using a WayNodeListBuilder. 337 */ opl_parse_way_nodes(const char * s,const char * e,osmium::memory::Buffer & buffer,osmium::builder::WayBuilder * parent_builder=nullptr)338 inline void opl_parse_way_nodes(const char* s, const char* e, osmium::memory::Buffer& buffer, osmium::builder::WayBuilder* parent_builder = nullptr) { 339 if (s == e) { 340 return; 341 } 342 osmium::builder::WayNodeListBuilder builder{buffer, parent_builder}; 343 344 while (s < e) { 345 opl_parse_char(&s, 'n'); 346 if (s == e) { 347 throw opl_error{"expected integer", s}; 348 } 349 350 const osmium::object_id_type ref = opl_parse_id(&s); 351 if (s == e) { 352 builder.add_node_ref(ref); 353 return; 354 } 355 356 osmium::Location location; 357 if (*s == 'x') { 358 ++s; 359 location.set_lon_partial(&s); 360 if (*s == 'y') { 361 ++s; 362 location.set_lat_partial(&s); 363 } 364 } 365 366 builder.add_node_ref(ref, location); 367 368 if (s == e) { 369 return; 370 } 371 372 opl_parse_char(&s, ','); 373 } 374 } 375 opl_parse_node(const char ** data,osmium::memory::Buffer & buffer)376 inline void opl_parse_node(const char** data, osmium::memory::Buffer& buffer) { 377 osmium::builder::NodeBuilder builder{buffer}; 378 379 builder.set_id(opl_parse_id(data)); 380 381 const char* tags_begin = nullptr; 382 383 bool has_version = false; 384 bool has_visible = false; 385 bool has_changeset_id = false; 386 bool has_timestamp = false; 387 bool has_uid = false; 388 bool has_user = false; 389 bool has_tags = false; 390 bool has_lon = false; 391 bool has_lat = false; 392 393 std::string user; 394 osmium::Location location; 395 while (**data) { 396 opl_parse_space(data); 397 const char c = **data; 398 if (c == '\0') { 399 break; 400 } 401 ++(*data); 402 switch (c) { 403 case 'v': 404 if (has_version) { 405 throw opl_error{"Duplicate attribute: version (v)"}; 406 } 407 has_version = true; 408 builder.set_version(opl_parse_version(data)); 409 break; 410 case 'd': 411 if (has_visible) { 412 throw opl_error{"Duplicate attribute: visible (d)"}; 413 } 414 has_visible = true; 415 builder.set_visible(opl_parse_visible(data)); 416 break; 417 case 'c': 418 if (has_changeset_id) { 419 throw opl_error{"Duplicate attribute: changeset_id (c)"}; 420 } 421 has_changeset_id = true; 422 builder.set_changeset(opl_parse_changeset_id(data)); 423 break; 424 case 't': 425 if (has_timestamp) { 426 throw opl_error{"Duplicate attribute: timestamp (t)"}; 427 } 428 has_timestamp = true; 429 builder.set_timestamp(opl_parse_timestamp(data)); 430 break; 431 case 'i': 432 if (has_uid) { 433 throw opl_error{"Duplicate attribute: uid (i)"}; 434 } 435 has_uid = true; 436 builder.set_uid(opl_parse_uid(data)); 437 break; 438 case 'u': 439 if (has_user) { 440 throw opl_error{"Duplicate attribute: user (u)"}; 441 } 442 has_user = true; 443 opl_parse_string(data, user); 444 break; 445 case 'T': 446 if (has_tags) { 447 throw opl_error{"Duplicate attribute: tags (T)"}; 448 } 449 has_tags = true; 450 if (opl_non_empty(*data)) { 451 tags_begin = *data; 452 opl_skip_section(data); 453 } 454 break; 455 case 'x': 456 if (has_lon) { 457 throw opl_error{"Duplicate attribute: lon (x)"}; 458 } 459 has_lon = true; 460 if (opl_non_empty(*data)) { 461 location.set_lon_partial(data); 462 } 463 break; 464 case 'y': 465 if (has_lat) { 466 throw opl_error{"Duplicate attribute: lat (y)"}; 467 } 468 has_lat = true; 469 if (opl_non_empty(*data)) { 470 location.set_lat_partial(data); 471 } 472 break; 473 default: 474 --(*data); 475 throw opl_error{"unknown attribute", *data}; 476 } 477 } 478 479 if (location.valid()) { 480 builder.set_location(location); 481 } 482 483 builder.set_user(user); 484 485 if (tags_begin) { 486 opl_parse_tags(tags_begin, buffer, &builder); 487 } 488 } 489 opl_parse_way(const char ** data,osmium::memory::Buffer & buffer)490 inline void opl_parse_way(const char** data, osmium::memory::Buffer& buffer) { 491 osmium::builder::WayBuilder builder{buffer}; 492 493 builder.set_id(opl_parse_id(data)); 494 495 const char* tags_begin = nullptr; 496 497 const char* nodes_begin = nullptr; 498 const char* nodes_end = nullptr; 499 500 bool has_version = false; 501 bool has_visible = false; 502 bool has_changeset_id = false; 503 bool has_timestamp = false; 504 bool has_uid = false; 505 bool has_user = false; 506 bool has_tags = false; 507 bool has_nodes = false; 508 509 std::string user; 510 while (**data) { 511 opl_parse_space(data); 512 const char c = **data; 513 if (c == '\0') { 514 break; 515 } 516 ++(*data); 517 switch (c) { 518 case 'v': 519 if (has_version) { 520 throw opl_error{"Duplicate attribute: version (v)"}; 521 } 522 has_version = true; 523 builder.set_version(opl_parse_version(data)); 524 break; 525 case 'd': 526 if (has_visible) { 527 throw opl_error{"Duplicate attribute: visible (d)"}; 528 } 529 has_visible = true; 530 builder.set_visible(opl_parse_visible(data)); 531 break; 532 case 'c': 533 if (has_changeset_id) { 534 throw opl_error{"Duplicate attribute: changeset_id (c)"}; 535 } 536 has_changeset_id = true; 537 builder.set_changeset(opl_parse_changeset_id(data)); 538 break; 539 case 't': 540 if (has_timestamp) { 541 throw opl_error{"Duplicate attribute: timestamp (t)"}; 542 } 543 has_timestamp = true; 544 builder.set_timestamp(opl_parse_timestamp(data)); 545 break; 546 case 'i': 547 if (has_uid) { 548 throw opl_error{"Duplicate attribute: uid (i)"}; 549 } 550 has_uid = true; 551 builder.set_uid(opl_parse_uid(data)); 552 break; 553 case 'u': 554 if (has_user) { 555 throw opl_error{"Duplicate attribute: user (u)"}; 556 } 557 has_user = true; 558 opl_parse_string(data, user); 559 break; 560 case 'T': 561 if (has_tags) { 562 throw opl_error{"Duplicate attribute: tags (T)"}; 563 } 564 has_tags = true; 565 if (opl_non_empty(*data)) { 566 tags_begin = *data; 567 opl_skip_section(data); 568 } 569 break; 570 case 'N': 571 if (has_nodes) { 572 throw opl_error{"Duplicate attribute: nodes (N)"}; 573 } 574 has_nodes = true; 575 nodes_begin = *data; 576 nodes_end = opl_skip_section(data); 577 break; 578 default: 579 --(*data); 580 throw opl_error{"unknown attribute", *data}; 581 } 582 } 583 584 builder.set_user(user); 585 586 if (tags_begin) { 587 opl_parse_tags(tags_begin, buffer, &builder); 588 } 589 590 opl_parse_way_nodes(nodes_begin, nodes_end, buffer, &builder); 591 } 592 opl_parse_relation_members(const char * s,const char * e,osmium::memory::Buffer & buffer,osmium::builder::RelationBuilder * parent_builder=nullptr)593 inline void opl_parse_relation_members(const char* s, const char* e, osmium::memory::Buffer& buffer, osmium::builder::RelationBuilder* parent_builder = nullptr) { 594 if (s == e) { 595 return; 596 } 597 osmium::builder::RelationMemberListBuilder builder{buffer, parent_builder}; 598 599 while (s < e) { 600 osmium::item_type type = osmium::char_to_item_type(*s); 601 if (type != osmium::item_type::node && 602 type != osmium::item_type::way && 603 type != osmium::item_type::relation) { 604 throw opl_error{"unknown object type", s}; 605 } 606 ++s; 607 608 if (s == e) { 609 throw opl_error{"expected integer", s}; 610 } 611 osmium::object_id_type ref = opl_parse_id(&s); 612 opl_parse_char(&s, '@'); 613 if (s == e) { 614 builder.add_member(type, ref, ""); 615 return; 616 } 617 std::string role; 618 opl_parse_string(&s, role); 619 builder.add_member(type, ref, role); 620 621 if (s == e) { 622 return; 623 } 624 opl_parse_char(&s, ','); 625 } 626 } 627 opl_parse_relation(const char ** data,osmium::memory::Buffer & buffer)628 inline void opl_parse_relation(const char** data, osmium::memory::Buffer& buffer) { 629 osmium::builder::RelationBuilder builder{buffer}; 630 631 builder.set_id(opl_parse_id(data)); 632 633 const char* tags_begin = nullptr; 634 635 const char* members_begin = nullptr; 636 const char* members_end = nullptr; 637 638 bool has_version = false; 639 bool has_visible = false; 640 bool has_changeset_id = false; 641 bool has_timestamp = false; 642 bool has_uid = false; 643 bool has_user = false; 644 bool has_tags = false; 645 bool has_members = false; 646 647 std::string user; 648 while (**data) { 649 opl_parse_space(data); 650 const char c = **data; 651 if (c == '\0') { 652 break; 653 } 654 ++(*data); 655 switch (c) { 656 case 'v': 657 if (has_version) { 658 throw opl_error{"Duplicate attribute: version (v)"}; 659 } 660 has_version = true; 661 builder.set_version(opl_parse_version(data)); 662 break; 663 case 'd': 664 if (has_visible) { 665 throw opl_error{"Duplicate attribute: visible (d)"}; 666 } 667 has_visible = true; 668 builder.set_visible(opl_parse_visible(data)); 669 break; 670 case 'c': 671 if (has_changeset_id) { 672 throw opl_error{"Duplicate attribute: changeset_id (c)"}; 673 } 674 has_changeset_id = true; 675 builder.set_changeset(opl_parse_changeset_id(data)); 676 break; 677 case 't': 678 if (has_timestamp) { 679 throw opl_error{"Duplicate attribute: timestamp (t)"}; 680 } 681 has_timestamp = true; 682 builder.set_timestamp(opl_parse_timestamp(data)); 683 break; 684 case 'i': 685 if (has_uid) { 686 throw opl_error{"Duplicate attribute: uid (i)"}; 687 } 688 has_uid = true; 689 builder.set_uid(opl_parse_uid(data)); 690 break; 691 case 'u': 692 if (has_user) { 693 throw opl_error{"Duplicate attribute: user (u)"}; 694 } 695 has_user = true; 696 opl_parse_string(data, user); 697 break; 698 case 'T': 699 if (has_tags) { 700 throw opl_error{"Duplicate attribute: tags (T)"}; 701 } 702 has_tags = true; 703 if (opl_non_empty(*data)) { 704 tags_begin = *data; 705 opl_skip_section(data); 706 } 707 break; 708 case 'M': 709 if (has_members) { 710 throw opl_error{"Duplicate attribute: members (M)"}; 711 } 712 has_members = true; 713 members_begin = *data; 714 members_end = opl_skip_section(data); 715 break; 716 default: 717 --(*data); 718 throw opl_error{"unknown attribute", *data}; 719 } 720 } 721 722 builder.set_user(user); 723 724 if (tags_begin) { 725 opl_parse_tags(tags_begin, buffer, &builder); 726 } 727 728 if (members_begin != members_end) { 729 opl_parse_relation_members(members_begin, members_end, buffer, &builder); 730 } 731 } 732 opl_parse_changeset(const char ** data,osmium::memory::Buffer & buffer)733 inline void opl_parse_changeset(const char** data, osmium::memory::Buffer& buffer) { 734 osmium::builder::ChangesetBuilder builder{buffer}; 735 736 builder.set_id(opl_parse_changeset_id(data)); 737 738 const char* tags_begin = nullptr; 739 740 bool has_num_changes = false; 741 bool has_created_at = false; 742 bool has_closed_at = false; 743 bool has_num_comments = false; 744 bool has_uid = false; 745 bool has_user = false; 746 bool has_tags = false; 747 bool has_min_x = false; 748 bool has_min_y = false; 749 bool has_max_x = false; 750 bool has_max_y = false; 751 752 osmium::Box box; 753 std::string user; 754 while (**data) { 755 opl_parse_space(data); 756 const char c = **data; 757 if (c == '\0') { 758 break; 759 } 760 ++(*data); 761 switch (c) { 762 case 'k': 763 if (has_num_changes) { 764 throw opl_error{"Duplicate attribute: num_changes (k)"}; 765 } 766 has_num_changes = true; 767 builder.set_num_changes(opl_parse_int<osmium::num_changes_type>(data)); 768 break; 769 case 's': 770 if (has_created_at) { 771 throw opl_error{"Duplicate attribute: created_at (s)"}; 772 } 773 has_created_at = true; 774 builder.set_created_at(opl_parse_timestamp(data)); 775 break; 776 case 'e': 777 if (has_closed_at) { 778 throw opl_error{"Duplicate attribute: closed_at (e)"}; 779 } 780 has_closed_at = true; 781 builder.set_closed_at(opl_parse_timestamp(data)); 782 break; 783 case 'd': 784 if (has_num_comments) { 785 throw opl_error{"Duplicate attribute: num_comments (d)"}; 786 } 787 has_num_comments = true; 788 builder.set_num_comments(opl_parse_int<osmium::num_comments_type>(data)); 789 break; 790 case 'i': 791 if (has_uid) { 792 throw opl_error{"Duplicate attribute: uid (i)"}; 793 } 794 has_uid = true; 795 builder.set_uid(opl_parse_uid(data)); 796 break; 797 case 'u': 798 if (has_user) { 799 throw opl_error{"Duplicate attribute: user (u)"}; 800 } 801 has_user = true; 802 opl_parse_string(data, user); 803 break; 804 case 'x': 805 if (has_min_x) { 806 throw opl_error{"Duplicate attribute: min_x (x)"}; 807 } 808 has_min_x = true; 809 if (opl_non_empty(*data)) { 810 box.bottom_left().set_lon_partial(data); 811 } 812 break; 813 case 'y': 814 if (has_min_y) { 815 throw opl_error{"Duplicate attribute: min_y (y)"}; 816 } 817 has_min_y = true; 818 if (opl_non_empty(*data)) { 819 box.bottom_left().set_lat_partial(data); 820 } 821 break; 822 case 'X': 823 if (has_max_x) { 824 throw opl_error{"Duplicate attribute: max_x (X)"}; 825 } 826 has_max_x = true; 827 if (opl_non_empty(*data)) { 828 box.top_right().set_lon_partial(data); 829 } 830 break; 831 case 'Y': 832 if (has_max_y) { 833 throw opl_error{"Duplicate attribute: max_y (Y)"}; 834 } 835 has_max_y = true; 836 if (opl_non_empty(*data)) { 837 box.top_right().set_lat_partial(data); 838 } 839 break; 840 case 'T': 841 if (has_tags) { 842 throw opl_error{"Duplicate attribute: tags (T)"}; 843 } 844 has_tags = true; 845 if (opl_non_empty(*data)) { 846 tags_begin = *data; 847 opl_skip_section(data); 848 } 849 break; 850 default: 851 --(*data); 852 throw opl_error{"unknown attribute", *data}; 853 } 854 855 } 856 857 builder.set_bounds(box); 858 builder.set_user(user); 859 860 if (tags_begin) { 861 opl_parse_tags(tags_begin, buffer, &builder); 862 } 863 } 864 opl_parse_line(uint64_t line_count,const char * data,osmium::memory::Buffer & buffer,osmium::osm_entity_bits::type read_types=osmium::osm_entity_bits::all)865 inline bool opl_parse_line(uint64_t line_count, 866 const char* data, 867 osmium::memory::Buffer& buffer, 868 osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all) { 869 const char* start_of_line = data; 870 try { 871 switch (*data) { 872 case '\0': 873 // ignore empty lines 874 break; 875 case '#': 876 // ignore lines starting with # 877 break; 878 case 'n': 879 if (read_types & osmium::osm_entity_bits::node) { 880 ++data; 881 opl_parse_node(&data, buffer); 882 buffer.commit(); 883 return true; 884 } 885 break; 886 case 'w': 887 if (read_types & osmium::osm_entity_bits::way) { 888 ++data; 889 opl_parse_way(&data, buffer); 890 buffer.commit(); 891 return true; 892 } 893 break; 894 case 'r': 895 if (read_types & osmium::osm_entity_bits::relation) { 896 ++data; 897 opl_parse_relation(&data, buffer); 898 buffer.commit(); 899 return true; 900 } 901 break; 902 case 'c': 903 if (read_types & osmium::osm_entity_bits::changeset) { 904 ++data; 905 opl_parse_changeset(&data, buffer); 906 buffer.commit(); 907 return true; 908 } 909 break; 910 default: 911 throw opl_error{"unknown type", data}; 912 } 913 } catch (opl_error& e) { 914 e.set_pos(line_count, e.data ? e.data - start_of_line : 0); 915 throw; 916 } 917 918 return false; 919 } 920 921 } // namespace detail 922 923 } // namespace io 924 925 } // namespace osmium 926 927 928 #endif // OSMIUM_IO_DETAIL_OPL_PARSER_FUNCTIONS_HPP 929