/src/lib/xml/dtd/xml_dtd_validator.e

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