/src/lib/xml/dtd/xml_dtd_validator.e
Specman e | 807 lines | 660 code | 93 blank | 54 comment | 23 complexity | 300b3a11ce3ac6e7217ef6e4e43b2fcd MD5 | raw file
1-- See the Copyright notice at the end of this file. 2-- 3class XML_DTD_VALIDATOR 4 -- 5 -- Helps the parser to validate an XML file using a DTD 6 -- 7 8inherit 9 XML_VALIDATOR 10 BACKTRACKING 11 rename 12 current_node as backtrack_node, 13 set_current_node as set_backtrack_node 14 end 15 16insert 17 XML_DTD_MEMORY 18 RECYCLABLE 19 20create {XML_DTD_MEMORY} 21 make 22 23feature {XML_PARSER} 24 attributes: HASHED_DICTIONARY[UNICODE_STRING, UNICODE_STRING] 25 26 root: XML_DTD_NODE 27 28 point: XML_DTD_NODE 29 30 with_attribute (attribute_name, attribute_value: UNICODE_STRING; line, column: INTEGER) 31 do 32 attributes.add(new_string(attribute_value), new_string(attribute_name)) 33 end 34 35 is_valid_open_node (node_name: UNICODE_STRING; line, column: INTEGER): BOOLEAN 36 do 37 if point = Void then 38 Result := root_element.name.is_equal(node_name) 39 else 40 Result := point.is_valid_child(Current, node_name) 41 end 42 if Result then 43 Result := get_element(node_name).is_valid_attributes(attributes) 44 end 45 end 46 47 is_valid_close_node (node_name: UNICODE_STRING; line, column: INTEGER): BOOLEAN 48 do 49 if point = Void then 50 -- not valid! 51 else 52 Result := point.name.is_equal(node_name) 53 and then point.is_valid_child(Current, Void) 54 end 55 end 56 57 is_valid_open_close_node (node_name: UNICODE_STRING; line, column: INTEGER): BOOLEAN 58 do 59 Result := is_valid_open_node(node_name, line, column) 60 end 61 62 current_node: UNICODE_STRING 63 do 64 if point /= Void then 65 Result := point.name 66 end 67 end 68 69 open_node (node_name: UNICODE_STRING; line, column: INTEGER) 70 local 71 e: XML_DTD_ELEMENT 72 do 73 clear_attributes 74 e := elements.at(node_name) 75 point := new_node(e, point) 76 if root = Void then 77 root := point 78 end 79 end 80 81 close_node (node_name: UNICODE_STRING; line, column: INTEGER) 82 local 83 p: like point 84 do 85 p := point.parent 86 if p = Void then 87 check 88 root = point 89 end 90 free_node(root) 91 root := Void 92 end 93 point := p 94 end 95 96 open_close_node (node_name: UNICODE_STRING; line, column: INTEGER) 97 do 98 open_node(node_name, line, column) 99 close_node(node_name, line, column) 100 end 101 102 entity (a_entity: UNICODE_STRING; line, column: INTEGER): UNICODE_STRING 103 do 104 Result := entities.reference_at(a_entity) 105 end 106 107 entity_url (a_entity: UNICODE_STRING; line, column: INTEGER): UNICODE_STRING 108 -- When the parser reads an '''&entity;'''. Returns the entity URL if it is a SYSTEM entity. 109 do 110 Result := entity_urls.reference_at(a_entity) 111 end 112 113 is_valid_data (a_data: UNICODE_STRING; line, column: INTEGER): BOOLEAN 114 do 115 if point /= Void then 116 Result := point.is_valid_data(Current, a_data) 117 end 118 end 119 120 data (a_data: UNICODE_STRING; line, column: INTEGER) 121 do 122 -- DTD does not validate data, so we don't keep it 123 end 124 125 the_end 126 do 127 recycle_dtd_validator(Current) 128 end 129 130feature {} -- Nodes management, for validation 131 nodes_pool: RECYCLING_POOL[XML_DTD_NODE] 132 once 133 create Result.make 134 end 135 136 new_node (element: XML_DTD_ELEMENT; parent: XML_DTD_NODE): XML_DTD_NODE 137 do 138 if nodes_pool.is_empty then 139 create Result.make 140 else 141 Result := nodes_pool.item 142 end 143 check 144 Result.parent = Void 145 end 146 Result.element := element 147 if parent /= Void then 148 Result.parent := parent 149 end 150 ensure 151 Result.is_empty 152 Result.element = element 153 Result.parent = parent 154 end 155 156 free_node (a_node: XML_DTD_NODE) 157 local 158 node: XML_DTD_NODE 159 do 160 from 161 until 162 a_node.is_empty 163 loop 164 node := a_node.last 165 free_node(node) 166 check 167 not a_node.fast_has(node) 168 end 169 end 170 nodes_pool.recycle(a_node) 171 end 172 173feature {} -- Attributes string management 174 clear_attributes 175 local 176 key, item: UNICODE_STRING 177 do 178 from 179 until 180 attributes.is_empty 181 loop 182 key := attributes.key(attributes.lower) 183 item := attributes.item(attributes.lower) 184 attributes.fast_remove(key) 185 free_string(key) 186 free_string(item) 187 end 188 end 189 190feature {XML_DTD_PARSER} 191 parse_done 192 -- Called when done parsing the DTD 193 do 194 debug 195 io.put_string(root_element.out) 196 io.put_new_line 197 end 198 end 199 200feature {XML_DTD_PARSER} -- <!ELEMENT . . .> 201 elements: HASHED_DICTIONARY[XML_DTD_ELEMENT, UNICODE_STRING] 202 203 current_element: XML_DTD_ELEMENT 204 205 element_built (element_name: UNICODE_STRING): BOOLEAN 206 do 207 Result := get_element(element_name).is_built 208 end 209 210 adding_element (element_name: UNICODE_STRING): BOOLEAN 211 do 212 Result := current_element /= Void and then current_element.name.is_equal(element_name) 213 ensure 214 Result implies not element_built(element_name) 215 Result implies building_element 216 end 217 218 building_element: BOOLEAN 219 do 220 Result := current_element /= Void 221 end 222 223 add_element (element_name: UNICODE_STRING) 224 require 225 not element_built(element_name) 226 not building_element 227 do 228 current_element := get_element(element_name) 229 current_element.build 230 ensure 231 adding_element(element_name) 232 end 233 234 commit_element (element_name: UNICODE_STRING) 235 require 236 adding_element(element_name) 237 do 238 current_element.commit 239 current_element := Void 240 ensure 241 element_built(element_name) 242 not building_element 243 end 244 245 close_fix 246 require 247 building_element 248 do 249 current_element.close_fix 250 end 251 252 close_exactly_one 253 require 254 building_element 255 do 256 current_element.close_exactly_one 257 end 258 259 close_zero_or_one 260 require 261 building_element 262 do 263 current_element.close_zero_or_one 264 end 265 266 close_zero_or_more 267 require 268 building_element 269 do 270 current_element.close_zero_or_more 271 end 272 273 close_one_or_more 274 require 275 building_element 276 do 277 current_element.close_one_or_more 278 end 279 280 add_list 281 require 282 building_element 283 do 284 current_element.add_list 285 end 286 287 add_alt 288 require 289 building_element 290 do 291 current_element.add_alt 292 end 293 294 child_pcdata 295 require 296 building_element 297 do 298 current_element.child_pcdata 299 end 300 301 child_any 302 require 303 building_element 304 do 305 current_element.child_any 306 end 307 308 child_empty 309 require 310 building_element 311 do 312 current_element.child_empty 313 end 314 315 child_one_or_more (node: UNICODE_STRING) 316 require 317 building_element 318 do 319 current_element.child_one_or_more(get_element(node)) 320 end 321 322 child_zero_or_more (node: UNICODE_STRING) 323 require 324 building_element 325 do 326 current_element.child_zero_or_more(get_element(node)) 327 end 328 329 child_zero_or_one (node: UNICODE_STRING) 330 require 331 building_element 332 do 333 current_element.child_zero_or_one(get_element(node)) 334 end 335 336 child_exactly_one (node: UNICODE_STRING) 337 require 338 building_element 339 do 340 current_element.child_exactly_one(get_element(node)) 341 end 342 343feature {} 344 root_element: XML_DTD_ELEMENT 345 do 346 Result := get_element(root_name) 347 end 348 349 get_element (element_name: UNICODE_STRING): XML_DTD_ELEMENT 350 local 351 eltname: UNICODE_STRING 352 do 353 Result := elements.reference_at(element_name) 354 if Result = Void then 355 eltname := element_name.twin 356 if elements_pool.is_empty then 357 create Result.make(eltname) 358 else 359 Result := elements_pool.item 360 Result.make(eltname) 361 end 362 elements.add(Result, eltname) 363 end 364 ensure 365 Result.name.is_equal(element_name) 366 end 367 368 elements_pool: RECYCLING_POOL[XML_DTD_ELEMENT] 369 once 370 create Result.make 371 end 372 373feature {XML_DTD_PARSER} -- <!ATTLIST . . .> 374 attlist_element: XML_DTD_ELEMENT 375 376 building_attlist: BOOLEAN 377 do 378 Result := attlist_element /= Void 379 check 380 Result implies attlist_element.building_attlist 381 end 382 end 383 384 has_attlist (element_name, attribute_name: UNICODE_STRING): BOOLEAN 385 local 386 elt: XML_DTD_ELEMENT 387 do 388 elt := elements.reference_at(element_name) 389 if elt /= Void then 390 Result := elt.has_attlist(attribute_name) 391 end 392 ensure 393 Result implies building_attlist 394 end 395 396 adding_attlist (element_name, attribute_name: UNICODE_STRING): BOOLEAN 397 do 398 Result := attlist_element /= Void and then attlist_element.name.is_equal(element_name) and then attlist_element.adding_attlist(attribute_name) 399 ensure 400 Result implies building_attlist 401 end 402 403 add_attlist (element_name, attribute_name: UNICODE_STRING) 404 require 405 element_built(element_name) 406 not building_element 407 not building_attlist 408 do 409 attlist_element := elements.at(element_name) 410 attlist_element.add_attlist(attribute_name) 411 ensure 412 building_attlist 413 end 414 415 commit_attlist (element_name, attribute_name: UNICODE_STRING) 416 require 417 adding_attlist(element_name, attribute_name) 418 do 419 attlist_element.commit_attlist(attribute_name) 420 attlist_element := Void 421 ensure 422 not building_attlist 423 end 424 425 addlist_list_value (value: UNICODE_STRING) 426 require 427 building_attlist 428 do 429 attlist_element.attlist_list_value(value) 430 end 431 432 attlist_cdata 433 require 434 building_attlist 435 do 436 attlist_element.attlist_cdata 437 end 438 439 attlist_id 440 require 441 building_attlist 442 do 443 attlist_element.attlist_id 444 end 445 446 attlist_idref 447 require 448 building_attlist 449 do 450 attlist_element.attlist_idref 451 end 452 453 attlist_idrefs 454 require 455 building_attlist 456 do 457 attlist_element.attlist_idrefs 458 end 459 460 attlist_nmtoken 461 require 462 building_attlist 463 do 464 attlist_element.attlist_nmtoken 465 end 466 467 attlist_nmtokens 468 require 469 building_attlist 470 do 471 attlist_element.attlist_nmtokens 472 end 473 474 attlist_entity 475 require 476 building_attlist 477 do 478 attlist_element.attlist_entity 479 end 480 481 attlist_entities 482 require 483 building_attlist 484 do 485 attlist_element.attlist_entities 486 end 487 488 attlist_notation 489 require 490 building_attlist 491 do 492 attlist_element.attlist_notation 493 end 494 495 attlist_required 496 require 497 building_attlist 498 do 499 attlist_element.attlist_required 500 end 501 502 attlist_implied 503 require 504 building_attlist 505 do 506 attlist_element.attlist_implied 507 end 508 509 attlist_valid_fixed (value: UNICODE_STRING): BOOLEAN 510 require 511 building_attlist 512 do 513 Result := attlist_element.attlist_valid_fixed(value) 514 end 515 516 attlist_fixed (value: UNICODE_STRING) 517 require 518 building_attlist 519 attlist_valid_fixed(value) 520 do 521 attlist_element.attlist_fixed(value) 522 end 523 524 attlist_default_value (value: UNICODE_STRING) 525 require 526 building_attlist 527 do 528 attlist_element.attlist_default_value(value) 529 end 530 531feature {XML_DTD_PARSER} -- <!ENTITY . . .> 532 entities: HASHED_DICTIONARY[UNICODE_STRING, UNICODE_STRING] 533 entity_urls: HASHED_DICTIONARY[UNICODE_STRING, UNICODE_STRING] 534 535 has_entity (entity_name: UNICODE_STRING): BOOLEAN 536 do 537 Result := entities.has(entity_name) 538 end 539 540 add_entity (entity_name, entity_value, entity__url: UNICODE_STRING) 541 require 542 not has_entity(entity_name) 543 do 544 entities.add(entity_value, entity_name) 545 if entity__url /= Void then 546 entity_urls.add(entity__url, entity_name) 547 end 548 ensure 549 has_entity(entity_name) 550 entity(entity_name, 0, 0) = entity_value 551 entity_url(entity_name, 0, 0) = entity__url 552 end 553 554feature {XML_DTD_ELEMENT} 555 backtrack_valid_data (a_children: FAST_ARRAY[XML_DTD_NODE]; a_node: like backtrack_node; a_data: like backtrack_pcdata): BOOLEAN 556 require 557 a_node /= Void 558 do 559 --| std_output.put_line("backtrack_valid_data: #(1) -> #(2) / #(3)" # a_node.out # a_children.out # (if a_data = Void then "" else a_data.out end)) 560 set_backtrack_node(a_node) 561 backtrack_pcdata := a_data 562 backtrack_children := a_children 563 backtrack_index := a_children.lower 564 backtrack_next := backtrack_next_pcdata_marker 565 backtrack_ok := False 566 search_first 567 Result := backtrack_ok 568 if not Result then 569 sedb_breakpoint 570 end 571 end 572 573 backtrack_is_valid (a_children: like backtrack_children; a_node: like backtrack_node; a_next: like backtrack_next): BOOLEAN 574 require 575 a_node /= Void 576 --| `a_next' can be Void, it means we are trying to close the node 577 do 578 --| std_output.put_line("backtrack_is_valid: #(1) -> #(2) / #(3)" # a_node.out # a_children.out # (if a_next = Void then "<none>" else a_next.out end)) 579 set_backtrack_node(a_node) 580 backtrack_pcdata := Void 581 backtrack_children := a_children 582 backtrack_index := a_children.lower 583 backtrack_next := a_next 584 backtrack_ok := False 585 search_first 586 Result := backtrack_ok 587 if not Result then 588 sedb_breakpoint 589 end 590 end 591 592feature {XML_DTD_ELEMENT} 593 backtrack_valid_child (elt: XML_DTD_ELEMENT) 594 require 595 elt /= Void 596 do 597 --| std_output.put_line(" backtrack_valid_child: #(1)" # elt.out) 598 if backtrack_next = backtrack_next_pcdata_marker then 599 --| std_output.put_line(" backtrack, expected PCDATA") 600 backtrack 601 elseif backtrack_children.valid_index(backtrack_index) then 602 if elt = backtrack_children.item(backtrack_index).element then 603 backtrack_index := backtrack_index + 1 604 --| std_output.put_line(" continue") 605 continue 606 else 607 --| std_output.put_line(" backtrack, actual #(1) but expected #(2)" # elt.out # backtrack_children.item(backtrack_index).element.out) 608 backtrack 609 end 610 else 611 if backtrack_next = Void then 612 --| std_output.put_line(" backtrack, unexpected next child" # backtrack_next.out) 613 backtrack 614 elseif elt.name.is_equal(backtrack_next) then 615 --| std_output.put_line(" FOUND") 616 backtrack_ok := True 617 stop_search_loop := True 618 else 619 --| std_output.put_line(" backtrack, next child is #(1) not #(2)" # backtrack_next.out # elt.name.out) 620 backtrack 621 end 622 end 623 end 624 625feature {XML_DTD_PCDATA_NODE} 626 backtrack_valid_pcdata 627 do 628 --| std_output.put_line(" backtrack_valid_pcdata") 629 if backtrack_children.valid_index(backtrack_index) then 630 backtrack_index := backtrack_index + 1 631 --| std_output.put_line(" continue") 632 continue 633 elseif backtrack_next = backtrack_next_pcdata_marker or else backtrack_next = Void then 634 --| std_output.put_line(" FOUND") 635 backtrack_ok := True 636 stop_search_loop := True 637 else 638 sedb_breakpoint 639 --| std_output.put_line(" backtrack") 640 backtrack 641 end 642 end 643 644feature {XML_DTD_ANY_NODE} 645 backtrack_valid_any 646 do 647 --| std_output.put_line(" backtrack_valid_any") 648 --| std_output.put_line(" FOUND") 649 backtrack_ok := True 650 stop_search_loop := True 651 end 652 653feature {XML_DTD_EMPTY_NODE} 654 backtrack_valid_empty 655 do 656 --| std_output.put_line(" backtrack_valid_empty") 657 if backtrack_children.is_empty and then (context.is_empty implies backtrack_next = Void) then 658 --| std_output.put_line(" FOUND") 659 backtrack_ok := True 660 stop_search_loop := True 661 else 662 --| std_output.put_line(" backtrack") 663 backtrack 664 end 665 end 666 667feature {XML_DTD_END_NODE} 668 backtrack_valid_end 669 do 670 --| std_output.put_line(" backtrack_valid_end") 671 if not backtrack_children.valid_index(backtrack_index) and then (context.is_empty implies backtrack_next = Void) then 672 --| std_output.put_line(" FOUND") 673 backtrack_ok := True 674 stop_search_loop := True 675 else 676 --| std_output.put_line(" backtrack") 677 backtrack 678 end 679 end 680 681feature {} -- Backtracking internals 682 backtrack_pcdata: UNICODE_STRING 683 backtrack_children: FAST_ARRAY[XML_DTD_NODE] 684 backtrack_index: INTEGER 685 backtrack_ok: BOOLEAN 686 backtrack_next: UNICODE_STRING 687 context: FAST_ARRAY[INTEGER] 688 689 context_clear 690 do 691 context.clear_count 692 end 693 694 context_push 695 do 696 --| std_output.put_line(" context push " + backtrack_index.out) 697 context.add_last(backtrack_index) 698 end 699 700 context_restore 701 do 702 backtrack_index := context.last 703 --| std_output.put_line(" context restore " + backtrack_index.out) 704 end 705 706 context_restore_and_pop 707 do 708 context_restore 709 context_cut 710 end 711 712 context_cut 713 do 714 --| std_output.put_line(" context cut ") 715 context.remove_last 716 end 717 718feature {XML_DTD_MEMORY} 719 root_name: UNICODE_STRING 720 721 make (root_node_name: like root_name) 722 require 723 not root_node_name.is_empty 724 do 725 root_name := root_node_name 726 if attributes = Void then 727 create attributes.make 728 create elements.make 729 create entities.make 730 create entity_urls.make 731 create context.make(0) 732 end 733 ensure 734 root_name = root_node_name 735 end 736 737feature {RECYCLING_POOL} 738 recycle 739 do 740 attributes.clear_count 741 recycle_elements 742 entities.clear_count 743 recycle_entity_urls 744 context.clear_count 745 end 746 747feature {} 748 recycle_elements 749 local 750 i: INTEGER 751 do 752 from 753 i := elements.lower 754 until 755 i > elements.upper 756 loop 757 elements_pool.recycle(elements.item(i)) 758 i := i + 1 759 end 760 elements.clear_count 761 ensure 762 elements.is_empty 763 end 764 765 recycle_entity_urls 766 local 767 i: INTEGER 768 do 769 from 770 i := entity_urls.lower 771 until 772 i > entity_urls.upper 773 loop 774 free_string(entity_urls.item(i)) 775 i := i + 1 776 end 777 entity_urls.clear_count 778 ensure 779 entity_urls.is_empty 780 end 781 782 backtrack_next_pcdata_marker: UNICODE_STRING once then U"__#PCDATA__" end 783 784invariant 785 not root_name.is_empty 786 787end -- class XML_DTD_VALIDATOR 788-- 789-- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file. 790-- 791-- Permission is hereby granted, free of charge, to any person obtaining a copy 792-- of this software and associated documentation files (the "Software"), to deal 793-- in the Software without restriction, including without limitation the rights 794-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 795-- copies of the Software, and to permit persons to whom the Software is 796-- furnished to do so, subject to the following conditions: 797-- 798-- The above copyright notice and this permission notice shall be included in 799-- all copies or substantial portions of the Software. 800-- 801-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 802-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 803-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 804-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 805-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 806-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 807-- THE SOFTWARE.