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 template <typename T> opl_parse_int(const char ** s)210 inline T opl_parse_int(const char** s) { 211 const bool negative = (**s == '-'); 212 if (negative) { 213 ++*s; 214 } 215 216 if (**s < '0' || **s > '9') { 217 throw opl_error{"expected integer", *s}; 218 } 219 220 int64_t value = 0; 221 while (**s >= '0' && **s <= '9') { 222 if (value <= -922337203685477580) { 223 if ((value < -922337203685477580) || (**s > '8')) { 224 throw opl_error("integer too long", *s); 225 } 226 } 227 value *= 10; 228 value -= **s - '0'; 229 ++*s; 230 } 231 232 if (negative) { 233 if (value < std::numeric_limits<T>::min()) { 234 throw opl_error{"integer too long", *s}; 235 } 236 } else { 237 if (value == std::numeric_limits<int64_t>::min()) { 238 throw opl_error{"integer too long", *s}; 239 } 240 value = -value; 241 if (value > std::numeric_limits<T>::max()) { 242 throw opl_error{"integer too long", *s}; 243 } 244 } 245 246 return T(value); 247 } 248 opl_parse_id(const char ** s)249 inline osmium::object_id_type opl_parse_id(const char** s) { 250 return opl_parse_int<osmium::object_id_type>(s); 251 } 252 opl_parse_changeset_id(const char ** s)253 inline osmium::changeset_id_type opl_parse_changeset_id(const char** s) { 254 return opl_parse_int<osmium::changeset_id_type>(s); 255 } 256 opl_parse_version(const char ** s)257 inline osmium::object_version_type opl_parse_version(const char** s) { 258 return opl_parse_int<osmium::object_version_type>(s); 259 } 260 opl_parse_visible(const char ** data)261 inline bool opl_parse_visible(const char** data) { 262 if (**data == 'V') { 263 ++*data; 264 return true; 265 } 266 if (**data == 'D') { 267 ++*data; 268 return false; 269 } 270 throw opl_error{"invalid visible flag", *data}; 271 } 272 opl_parse_uid(const char ** s)273 inline osmium::user_id_type opl_parse_uid(const char** s) { 274 return opl_parse_int<osmium::user_id_type>(s); 275 } 276 opl_parse_timestamp(const char ** s)277 inline osmium::Timestamp opl_parse_timestamp(const char** s) { 278 try { 279 if (**s == '\0' || **s == ' ' || **s == '\t') { 280 return osmium::Timestamp{}; 281 } 282 osmium::Timestamp timestamp{*s}; 283 *s += 20; 284 return timestamp; 285 } catch (const std::invalid_argument&) { 286 throw opl_error{"can not parse timestamp", *s}; 287 } 288 } 289 290 /** 291 * Check if data points to given character and consume it. 292 * Throw error otherwise. 293 */ opl_parse_char(const char ** data,char c)294 inline void opl_parse_char(const char** data, char c) { 295 if (**data == c) { 296 ++*data; 297 return; 298 } 299 std::string msg{"expected '"}; 300 msg += c; 301 msg += "'"; 302 throw opl_error{msg, *data}; 303 } 304 305 /** 306 * Parse a list of tags in the format 'key=value,key=value,...' 307 * 308 * Tags will be added to the buffer using a TagListBuilder. 309 */ opl_parse_tags(const char * s,osmium::memory::Buffer & buffer,osmium::builder::Builder * parent_builder=nullptr)310 inline void opl_parse_tags(const char* s, osmium::memory::Buffer& buffer, osmium::builder::Builder* parent_builder = nullptr) { 311 osmium::builder::TagListBuilder builder{buffer, parent_builder}; 312 std::string key; 313 std::string value; 314 while (true) { 315 opl_parse_string(&s, key); 316 opl_parse_char(&s, '='); 317 opl_parse_string(&s, value); 318 builder.add_tag(key, value); 319 if (*s == ' ' || *s == '\t' || *s == '\0') { 320 break; 321 } 322 opl_parse_char(&s, ','); 323 key.clear(); 324 value.clear(); 325 } 326 } 327 328 /** 329 * Parse a number of nodes in the format "nID,nID,nID..." 330 * 331 * Nodes will be added to the buffer using a WayNodeListBuilder. 332 */ opl_parse_way_nodes(const char * s,const char * e,osmium::memory::Buffer & buffer,osmium::builder::WayBuilder * parent_builder=nullptr)333 inline void opl_parse_way_nodes(const char* s, const char* e, osmium::memory::Buffer& buffer, osmium::builder::WayBuilder* parent_builder = nullptr) { 334 if (s == e) { 335 return; 336 } 337 osmium::builder::WayNodeListBuilder builder{buffer, parent_builder}; 338 339 while (s < e) { 340 opl_parse_char(&s, 'n'); 341 if (s == e) { 342 throw opl_error{"expected integer", s}; 343 } 344 345 const osmium::object_id_type ref = opl_parse_id(&s); 346 if (s == e) { 347 builder.add_node_ref(ref); 348 return; 349 } 350 351 osmium::Location location; 352 if (*s == 'x') { 353 ++s; 354 location.set_lon_partial(&s); 355 if (*s == 'y') { 356 ++s; 357 location.set_lat_partial(&s); 358 } 359 } 360 361 builder.add_node_ref(ref, location); 362 363 if (s == e) { 364 return; 365 } 366 367 opl_parse_char(&s, ','); 368 } 369 } 370 opl_parse_node(const char ** data,osmium::memory::Buffer & buffer)371 inline void opl_parse_node(const char** data, osmium::memory::Buffer& buffer) { 372 osmium::builder::NodeBuilder builder{buffer}; 373 374 builder.set_id(opl_parse_id(data)); 375 376 const char* tags_begin = nullptr; 377 378 bool has_version = false; 379 bool has_visible = false; 380 bool has_changeset_id = false; 381 bool has_timestamp = false; 382 bool has_uid = false; 383 bool has_user = false; 384 bool has_tags = false; 385 bool has_lon = false; 386 bool has_lat = false; 387 388 std::string user; 389 osmium::Location location; 390 while (**data) { 391 opl_parse_space(data); 392 const char c = **data; 393 if (c == '\0') { 394 break; 395 } 396 ++(*data); 397 switch (c) { 398 case 'v': 399 if (has_version) { 400 throw opl_error{"Duplicate attribute: version (v)"}; 401 } 402 has_version = true; 403 builder.set_version(opl_parse_version(data)); 404 break; 405 case 'd': 406 if (has_visible) { 407 throw opl_error{"Duplicate attribute: visible (d)"}; 408 } 409 has_visible = true; 410 builder.set_visible(opl_parse_visible(data)); 411 break; 412 case 'c': 413 if (has_changeset_id) { 414 throw opl_error{"Duplicate attribute: changeset_id (c)"}; 415 } 416 has_changeset_id = true; 417 builder.set_changeset(opl_parse_changeset_id(data)); 418 break; 419 case 't': 420 if (has_timestamp) { 421 throw opl_error{"Duplicate attribute: timestamp (t)"}; 422 } 423 has_timestamp = true; 424 builder.set_timestamp(opl_parse_timestamp(data)); 425 break; 426 case 'i': 427 if (has_uid) { 428 throw opl_error{"Duplicate attribute: uid (i)"}; 429 } 430 has_uid = true; 431 builder.set_uid(opl_parse_uid(data)); 432 break; 433 case 'u': 434 if (has_user) { 435 throw opl_error{"Duplicate attribute: user (u)"}; 436 } 437 has_user = true; 438 opl_parse_string(data, user); 439 break; 440 case 'T': 441 if (has_tags) { 442 throw opl_error{"Duplicate attribute: tags (T)"}; 443 } 444 has_tags = true; 445 if (opl_non_empty(*data)) { 446 tags_begin = *data; 447 opl_skip_section(data); 448 } 449 break; 450 case 'x': 451 if (has_lon) { 452 throw opl_error{"Duplicate attribute: lon (x)"}; 453 } 454 has_lon = true; 455 if (opl_non_empty(*data)) { 456 location.set_lon_partial(data); 457 } 458 break; 459 case 'y': 460 if (has_lat) { 461 throw opl_error{"Duplicate attribute: lat (y)"}; 462 } 463 has_lat = true; 464 if (opl_non_empty(*data)) { 465 location.set_lat_partial(data); 466 } 467 break; 468 default: 469 --(*data); 470 throw opl_error{"unknown attribute", *data}; 471 } 472 } 473 474 if (location.valid()) { 475 builder.set_location(location); 476 } 477 478 builder.set_user(user); 479 480 if (tags_begin) { 481 opl_parse_tags(tags_begin, buffer, &builder); 482 } 483 } 484 opl_parse_way(const char ** data,osmium::memory::Buffer & buffer)485 inline void opl_parse_way(const char** data, osmium::memory::Buffer& buffer) { 486 osmium::builder::WayBuilder builder{buffer}; 487 488 builder.set_id(opl_parse_id(data)); 489 490 const char* tags_begin = nullptr; 491 492 const char* nodes_begin = nullptr; 493 const char* nodes_end = nullptr; 494 495 bool has_version = false; 496 bool has_visible = false; 497 bool has_changeset_id = false; 498 bool has_timestamp = false; 499 bool has_uid = false; 500 bool has_user = false; 501 bool has_tags = false; 502 bool has_nodes = false; 503 504 std::string user; 505 while (**data) { 506 opl_parse_space(data); 507 const char c = **data; 508 if (c == '\0') { 509 break; 510 } 511 ++(*data); 512 switch (c) { 513 case 'v': 514 if (has_version) { 515 throw opl_error{"Duplicate attribute: version (v)"}; 516 } 517 has_version = true; 518 builder.set_version(opl_parse_version(data)); 519 break; 520 case 'd': 521 if (has_visible) { 522 throw opl_error{"Duplicate attribute: visible (d)"}; 523 } 524 has_visible = true; 525 builder.set_visible(opl_parse_visible(data)); 526 break; 527 case 'c': 528 if (has_changeset_id) { 529 throw opl_error{"Duplicate attribute: changeset_id (c)"}; 530 } 531 has_changeset_id = true; 532 builder.set_changeset(opl_parse_changeset_id(data)); 533 break; 534 case 't': 535 if (has_timestamp) { 536 throw opl_error{"Duplicate attribute: timestamp (t)"}; 537 } 538 has_timestamp = true; 539 builder.set_timestamp(opl_parse_timestamp(data)); 540 break; 541 case 'i': 542 if (has_uid) { 543 throw opl_error{"Duplicate attribute: uid (i)"}; 544 } 545 has_uid = true; 546 builder.set_uid(opl_parse_uid(data)); 547 break; 548 case 'u': 549 if (has_user) { 550 throw opl_error{"Duplicate attribute: user (u)"}; 551 } 552 has_user = true; 553 opl_parse_string(data, user); 554 break; 555 case 'T': 556 if (has_tags) { 557 throw opl_error{"Duplicate attribute: tags (T)"}; 558 } 559 has_tags = true; 560 if (opl_non_empty(*data)) { 561 tags_begin = *data; 562 opl_skip_section(data); 563 } 564 break; 565 case 'N': 566 if (has_nodes) { 567 throw opl_error{"Duplicate attribute: nodes (N)"}; 568 } 569 has_nodes = true; 570 nodes_begin = *data; 571 nodes_end = opl_skip_section(data); 572 break; 573 default: 574 --(*data); 575 throw opl_error{"unknown attribute", *data}; 576 } 577 } 578 579 builder.set_user(user); 580 581 if (tags_begin) { 582 opl_parse_tags(tags_begin, buffer, &builder); 583 } 584 585 opl_parse_way_nodes(nodes_begin, nodes_end, buffer, &builder); 586 } 587 opl_parse_relation_members(const char * s,const char * e,osmium::memory::Buffer & buffer,osmium::builder::RelationBuilder * parent_builder=nullptr)588 inline void opl_parse_relation_members(const char* s, const char* e, osmium::memory::Buffer& buffer, osmium::builder::RelationBuilder* parent_builder = nullptr) { 589 if (s == e) { 590 return; 591 } 592 osmium::builder::RelationMemberListBuilder builder{buffer, parent_builder}; 593 594 while (s < e) { 595 osmium::item_type type = osmium::char_to_item_type(*s); 596 if (type != osmium::item_type::node && 597 type != osmium::item_type::way && 598 type != osmium::item_type::relation) { 599 throw opl_error{"unknown object type", s}; 600 } 601 ++s; 602 603 if (s == e) { 604 throw opl_error{"expected integer", s}; 605 } 606 osmium::object_id_type ref = opl_parse_id(&s); 607 opl_parse_char(&s, '@'); 608 if (s == e) { 609 builder.add_member(type, ref, ""); 610 return; 611 } 612 std::string role; 613 opl_parse_string(&s, role); 614 builder.add_member(type, ref, role); 615 616 if (s == e) { 617 return; 618 } 619 opl_parse_char(&s, ','); 620 } 621 } 622 opl_parse_relation(const char ** data,osmium::memory::Buffer & buffer)623 inline void opl_parse_relation(const char** data, osmium::memory::Buffer& buffer) { 624 osmium::builder::RelationBuilder builder{buffer}; 625 626 builder.set_id(opl_parse_id(data)); 627 628 const char* tags_begin = nullptr; 629 630 const char* members_begin = nullptr; 631 const char* members_end = nullptr; 632 633 bool has_version = false; 634 bool has_visible = false; 635 bool has_changeset_id = false; 636 bool has_timestamp = false; 637 bool has_uid = false; 638 bool has_user = false; 639 bool has_tags = false; 640 bool has_members = false; 641 642 std::string user; 643 while (**data) { 644 opl_parse_space(data); 645 const char c = **data; 646 if (c == '\0') { 647 break; 648 } 649 ++(*data); 650 switch (c) { 651 case 'v': 652 if (has_version) { 653 throw opl_error{"Duplicate attribute: version (v)"}; 654 } 655 has_version = true; 656 builder.set_version(opl_parse_version(data)); 657 break; 658 case 'd': 659 if (has_visible) { 660 throw opl_error{"Duplicate attribute: visible (d)"}; 661 } 662 has_visible = true; 663 builder.set_visible(opl_parse_visible(data)); 664 break; 665 case 'c': 666 if (has_changeset_id) { 667 throw opl_error{"Duplicate attribute: changeset_id (c)"}; 668 } 669 has_changeset_id = true; 670 builder.set_changeset(opl_parse_changeset_id(data)); 671 break; 672 case 't': 673 if (has_timestamp) { 674 throw opl_error{"Duplicate attribute: timestamp (t)"}; 675 } 676 has_timestamp = true; 677 builder.set_timestamp(opl_parse_timestamp(data)); 678 break; 679 case 'i': 680 if (has_uid) { 681 throw opl_error{"Duplicate attribute: uid (i)"}; 682 } 683 has_uid = true; 684 builder.set_uid(opl_parse_uid(data)); 685 break; 686 case 'u': 687 if (has_user) { 688 throw opl_error{"Duplicate attribute: user (u)"}; 689 } 690 has_user = true; 691 opl_parse_string(data, user); 692 break; 693 case 'T': 694 if (has_tags) { 695 throw opl_error{"Duplicate attribute: tags (T)"}; 696 } 697 has_tags = true; 698 if (opl_non_empty(*data)) { 699 tags_begin = *data; 700 opl_skip_section(data); 701 } 702 break; 703 case 'M': 704 if (has_members) { 705 throw opl_error{"Duplicate attribute: members (M)"}; 706 } 707 has_members = true; 708 members_begin = *data; 709 members_end = opl_skip_section(data); 710 break; 711 default: 712 --(*data); 713 throw opl_error{"unknown attribute", *data}; 714 } 715 } 716 717 builder.set_user(user); 718 719 if (tags_begin) { 720 opl_parse_tags(tags_begin, buffer, &builder); 721 } 722 723 if (members_begin != members_end) { 724 opl_parse_relation_members(members_begin, members_end, buffer, &builder); 725 } 726 } 727 opl_parse_changeset(const char ** data,osmium::memory::Buffer & buffer)728 inline void opl_parse_changeset(const char** data, osmium::memory::Buffer& buffer) { 729 osmium::builder::ChangesetBuilder builder{buffer}; 730 731 builder.set_id(opl_parse_changeset_id(data)); 732 733 const char* tags_begin = nullptr; 734 735 bool has_num_changes = false; 736 bool has_created_at = false; 737 bool has_closed_at = false; 738 bool has_num_comments = false; 739 bool has_uid = false; 740 bool has_user = false; 741 bool has_tags = false; 742 bool has_min_x = false; 743 bool has_min_y = false; 744 bool has_max_x = false; 745 bool has_max_y = false; 746 747 osmium::Box box; 748 std::string user; 749 while (**data) { 750 opl_parse_space(data); 751 const char c = **data; 752 if (c == '\0') { 753 break; 754 } 755 ++(*data); 756 switch (c) { 757 case 'k': 758 if (has_num_changes) { 759 throw opl_error{"Duplicate attribute: num_changes (k)"}; 760 } 761 has_num_changes = true; 762 builder.set_num_changes(opl_parse_int<osmium::num_changes_type>(data)); 763 break; 764 case 's': 765 if (has_created_at) { 766 throw opl_error{"Duplicate attribute: created_at (s)"}; 767 } 768 has_created_at = true; 769 builder.set_created_at(opl_parse_timestamp(data)); 770 break; 771 case 'e': 772 if (has_closed_at) { 773 throw opl_error{"Duplicate attribute: closed_at (e)"}; 774 } 775 has_closed_at = true; 776 builder.set_closed_at(opl_parse_timestamp(data)); 777 break; 778 case 'd': 779 if (has_num_comments) { 780 throw opl_error{"Duplicate attribute: num_comments (d)"}; 781 } 782 has_num_comments = true; 783 builder.set_num_comments(opl_parse_int<osmium::num_comments_type>(data)); 784 break; 785 case 'i': 786 if (has_uid) { 787 throw opl_error{"Duplicate attribute: uid (i)"}; 788 } 789 has_uid = true; 790 builder.set_uid(opl_parse_uid(data)); 791 break; 792 case 'u': 793 if (has_user) { 794 throw opl_error{"Duplicate attribute: user (u)"}; 795 } 796 has_user = true; 797 opl_parse_string(data, user); 798 break; 799 case 'x': 800 if (has_min_x) { 801 throw opl_error{"Duplicate attribute: min_x (x)"}; 802 } 803 has_min_x = true; 804 if (opl_non_empty(*data)) { 805 box.bottom_left().set_lon_partial(data); 806 } 807 break; 808 case 'y': 809 if (has_min_y) { 810 throw opl_error{"Duplicate attribute: min_y (y)"}; 811 } 812 has_min_y = true; 813 if (opl_non_empty(*data)) { 814 box.bottom_left().set_lat_partial(data); 815 } 816 break; 817 case 'X': 818 if (has_max_x) { 819 throw opl_error{"Duplicate attribute: max_x (X)"}; 820 } 821 has_max_x = true; 822 if (opl_non_empty(*data)) { 823 box.top_right().set_lon_partial(data); 824 } 825 break; 826 case 'Y': 827 if (has_max_y) { 828 throw opl_error{"Duplicate attribute: max_y (Y)"}; 829 } 830 has_max_y = true; 831 if (opl_non_empty(*data)) { 832 box.top_right().set_lat_partial(data); 833 } 834 break; 835 case 'T': 836 if (has_tags) { 837 throw opl_error{"Duplicate attribute: tags (T)"}; 838 } 839 has_tags = true; 840 if (opl_non_empty(*data)) { 841 tags_begin = *data; 842 opl_skip_section(data); 843 } 844 break; 845 default: 846 --(*data); 847 throw opl_error{"unknown attribute", *data}; 848 } 849 850 } 851 852 builder.set_bounds(box); 853 builder.set_user(user); 854 855 if (tags_begin) { 856 opl_parse_tags(tags_begin, buffer, &builder); 857 } 858 } 859 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)860 inline bool opl_parse_line(uint64_t line_count, 861 const char* data, 862 osmium::memory::Buffer& buffer, 863 osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all) { 864 const char* start_of_line = data; 865 try { 866 switch (*data) { 867 case '\0': 868 // ignore empty lines 869 break; 870 case '#': 871 // ignore lines starting with # 872 break; 873 case 'n': 874 if (read_types & osmium::osm_entity_bits::node) { 875 ++data; 876 opl_parse_node(&data, buffer); 877 buffer.commit(); 878 return true; 879 } 880 break; 881 case 'w': 882 if (read_types & osmium::osm_entity_bits::way) { 883 ++data; 884 opl_parse_way(&data, buffer); 885 buffer.commit(); 886 return true; 887 } 888 break; 889 case 'r': 890 if (read_types & osmium::osm_entity_bits::relation) { 891 ++data; 892 opl_parse_relation(&data, buffer); 893 buffer.commit(); 894 return true; 895 } 896 break; 897 case 'c': 898 if (read_types & osmium::osm_entity_bits::changeset) { 899 ++data; 900 opl_parse_changeset(&data, buffer); 901 buffer.commit(); 902 return true; 903 } 904 break; 905 default: 906 throw opl_error{"unknown type", data}; 907 } 908 } catch (opl_error& e) { 909 e.set_pos(line_count, e.data ? e.data - start_of_line : 0); 910 throw; 911 } 912 913 return false; 914 } 915 916 } // namespace detail 917 918 } // namespace io 919 920 } // namespace osmium 921 922 923 #endif // OSMIUM_IO_DETAIL_OPL_PARSER_FUNCTIONS_HPP 924