/src/lib/xml/namespaces/xmlns_parser.e

http://github.com/tybor/Liberty · Specman e · 462 lines · 387 code · 40 blank · 35 comment · 38 complexity · dc104d87507c9bcae1eaa70486679035 MD5 · raw file

  1. -- See the Copyright notice at the end of this file.
  2. --
  3. class XMLNS_PARSER
  4. --
  5. -- A namespace-aware XML parser
  6. --
  7. inherit
  8. XML_CALLBACKS
  9. rename
  10. validator as xml_validator
  11. set_validator as set_xml_validator
  12. end
  13. insert
  14. XML_NAMESPACES
  15. create {ANY}
  16. connect_to, make
  17. feature {ANY}
  18. parse (a_callbacks: XMLNS_CALLBACKS)
  19. -- Parse an XML documents by sending parsing events to the given `callbacks'
  20. require
  21. is_connected
  22. do
  23. callbacks := a_callbacks
  24. parser.parse(Current)
  25. if validator /= Void then
  26. validator.the_end
  27. end
  28. end
  29. connect_to (a_url: URL)
  30. -- Connect to the given XML document
  31. require
  32. not is_connected
  33. a_url.is_connected implies a_url.read
  34. do
  35. make
  36. parser.connect_to(a_url)
  37. end
  38. disconnect
  39. require
  40. is_connected
  41. do
  42. parser.disconnect
  43. ensure
  44. not is_connected
  45. end
  46. is_connected: BOOLEAN
  47. do
  48. Result := parser.is_connected
  49. end
  50. line: INTEGER
  51. do
  52. Result := parser.line
  53. end
  54. column: INTEGER
  55. do
  56. Result := parser.column
  57. end
  58. feature {}
  59. make
  60. -- Create a not connected parser
  61. do
  62. if parser = Void then -- this test is useful when called from `connect_to'
  63. create parser.make
  64. create namespaces.make(0)
  65. else
  66. from
  67. until
  68. namespaces.is_empty
  69. loop
  70. old_namespaces(namespaces.last)
  71. namespaces.remove_last
  72. end
  73. end
  74. attributes_for_new_node := False
  75. end
  76. parser: XML_PARSER
  77. callbacks: XMLNS_CALLBACKS
  78. feature {XML_NAMESPACES}
  79. set_validator (a_validator: XMLNS_VALIDATOR)
  80. do
  81. callbacks.set_validator(a_validator)
  82. end
  83. validator: XMLNS_VALIDATOR
  84. do
  85. Result := callbacks.validator
  86. end
  87. feature {XML_PARSER}
  88. with_attribute (attribute_name: UNICODE_STRING; attribute_value: UNICODE_STRING; l, c: INTEGER)
  89. do
  90. if not attributes_for_new_node then
  91. namespaces.add_last(new_namespaces)
  92. attributes_for_new_node := True
  93. end
  94. if attribute_name.has_prefix(once U"xml") then
  95. xml_attribute(attribute_name, attribute_value, l, c)
  96. else
  97. split_namespace(attribute_name, l, c)
  98. if not at_error then
  99. if validator /= Void then
  100. validator.with_attribute(namespace, name, attribute_value, l, c)
  101. end
  102. callbacks.with_attribute(namespace, name, attribute_value, l, c)
  103. end
  104. end
  105. end
  106. open_node (node_name: UNICODE_STRING; l, c: INTEGER)
  107. do
  108. if attributes_for_new_node then
  109. attributes_for_new_node := False
  110. else
  111. namespaces.add_last(new_namespaces)
  112. end
  113. split_namespace(node_name, l, c)
  114. if not at_error then
  115. if validator = Void then
  116. callbacks.open_node(namespace, name, l, c)
  117. elseif validator.is_valid_open_node(namespace, name, l, c) then
  118. validator.open_node(namespace, name, l, c)
  119. callbacks.open_node(namespace, name, l, c)
  120. else
  121. parse_error(l, c, once "Invalid opening tag")
  122. end
  123. end
  124. end
  125. close_node (node_name: UNICODE_STRING; l, c: INTEGER)
  126. do
  127. split_namespace(node_name, l, c)
  128. if not at_error then
  129. if validator = Void then
  130. callbacks.close_node(namespace, name, l, c)
  131. elseif validator.is_valid_close_node(namespace, name, l, c) then
  132. validator.close_node(namespace, name, l, c)
  133. callbacks.close_node(namespace, name, l, c)
  134. else
  135. parse_error(l, c, once "Invalid closing tag")
  136. end
  137. old_namespaces(namespaces.last)
  138. namespaces.remove_last
  139. end
  140. end
  141. open_close_node (node_name: UNICODE_STRING; l, c: INTEGER)
  142. local
  143. local_namespaces: BOOLEAN
  144. do
  145. if attributes_for_new_node then
  146. attributes_for_new_node := False
  147. local_namespaces := True
  148. end
  149. split_namespace(node_name, l, c)
  150. if not at_error then
  151. if validator = Void then
  152. callbacks.open_close_node(namespace, name, l, c)
  153. elseif validator.is_valid_open_close_node(namespace, name, l, c) then
  154. validator.open_close_node(namespace, name, l, c)
  155. callbacks.open_close_node(namespace, name, l, c)
  156. else
  157. parse_error(l, c, once "Invalid empty tag")
  158. end
  159. if local_namespaces then
  160. old_namespaces(namespaces.last)
  161. namespaces.remove_last
  162. end
  163. end
  164. end
  165. xml_header (l, c: INTEGER)
  166. do
  167. callbacks.xml_header(l, c)
  168. end
  169. processing_instruction (a_target, a_data: UNICODE_STRING)
  170. do
  171. callbacks.processing_instruction(a_target, a_data)
  172. end
  173. entity (a_entity: UNICODE_STRING; l, c: INTEGER): UNICODE_STRING
  174. do
  175. if validator = Void then
  176. Result := callbacks.entity(a_entity, l, c)
  177. else
  178. Result := validator.entity(a_entity, l, c)
  179. if Result = Void then
  180. Result := callbacks.entity(a_entity, l, c)
  181. end
  182. end
  183. end
  184. current_node: UNICODE_STRING
  185. local
  186. ns, cn: UNICODE_STRING
  187. do
  188. cn := callbacks.current_node
  189. if cn /= Void then
  190. Result := once U""
  191. ns := callbacks.current_namespace
  192. if ns = Void or else ns.is_empty then -- Void only when at error
  193. Result.copy(cn)
  194. else
  195. Result.copy(find_namespace_ref(ns))
  196. Result.extend(':'.code)
  197. Result.append(cn)
  198. end
  199. end
  200. end
  201. data (a_data: UNICODE_STRING; l, c: INTEGER)
  202. do
  203. if validator = Void then
  204. callbacks.data(a_data, l, c)
  205. elseif validator.is_valid_data(a_data, l, c) then
  206. validator.data(a_data, l, c)
  207. callbacks.data(a_data, l, c)
  208. else
  209. parse_error(l, c, once "Invalid data")
  210. end
  211. end
  212. parse_error (l, c: INTEGER; message: STRING)
  213. do
  214. callbacks.parse_error(l, c, message)
  215. end
  216. at_error: BOOLEAN
  217. do
  218. Result := callbacks.at_error
  219. end
  220. feature {}
  221. namespace: UNICODE_STRING
  222. -- set by `split_namespace'
  223. name: UNICODE_STRING
  224. -- set by `split_namespace'
  225. split_namespace (a_name: UNICODE_STRING; l, c: INTEGER)
  226. -- Sets `namespace' and `name' according to the given name, splitting at the first colon (':'). If
  227. -- there is no colon, `namespace' is Void and `name' contains the full given name. Otherwise
  228. -- `namespace' contains the URI of the namespace and `name' contains the part of the given name after
  229. -- the first colon.
  230. local
  231. i: INTEGER; ns: UNICODE_STRING; error: STRING
  232. do
  233. name := once U""
  234. i := a_name.first_index_of(':'.code)
  235. if a_name.valid_index(i) then
  236. ns := once U""
  237. ns.copy_substring(a_name, 1, i - 1)
  238. name.copy_substring(a_name, i + 1, a_name.upper)
  239. namespace := find_namespace(ns)
  240. if namespace = Void then
  241. error := once ""
  242. error.copy(once "Unknown namespace prefix: ")
  243. ns.utf8_encode_in(error)
  244. parse_error(l, c, error)
  245. end
  246. else
  247. name.copy(a_name)
  248. namespace := Void
  249. end
  250. end
  251. namespaces: FAST_ARRAY[BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING]]
  252. -- The known namespaces
  253. xml_attribute (attribute_name, attribute_value: UNICODE_STRING; l, c: INTEGER)
  254. require
  255. attribute_name /= Void
  256. local
  257. old_value, ns: UNICODE_STRING; error: STRING
  258. action: PROCEDURE[TUPLE[XMLNS_PARSER, UNICODE_STRING]]
  259. do
  260. if attribute_name.is_equal(once U"xmlns") then
  261. ns := once U""
  262. check
  263. ns.is_empty
  264. end
  265. elseif attribute_name.has_prefix(once U"xmlns:") then
  266. ns := once U""
  267. ns.copy_substring(attribute_name, 7, attribute_name.upper)
  268. end
  269. if ns /= Void then
  270. action := namespace_actions.reference_at(attribute_value)
  271. if action /= Void then
  272. action.call([Current, ns])
  273. end
  274. old_value := namespaces.last.reference_at(ns)
  275. if old_value /= Void then
  276. string_recycle(namespaces.last.key_at(old_value))
  277. string_recycle(old_value)
  278. namespaces.last.remove(ns)
  279. end
  280. namespaces.last.add(string_twin(attribute_value), string_twin(ns))
  281. check
  282. find_namespace(ns).is_equal(attribute_value)
  283. end
  284. else
  285. error := once ""
  286. error.copy(once "Bad namespace prefix (must not begin by %"xml%") for attribute: %"")
  287. attribute_name.utf8_encode_in(error)
  288. error.extend('"')
  289. parse_error(l, c, error)
  290. end
  291. end
  292. find_namespace (a_namespace_ref: UNICODE_STRING): UNICODE_STRING
  293. local
  294. i: INTEGER
  295. do
  296. from
  297. i := namespaces.upper
  298. until
  299. Result /= Void or else i < namespaces.lower
  300. loop
  301. Result := namespaces.item(i).reference_at(a_namespace_ref)
  302. i := i - 1
  303. end
  304. end
  305. find_namespace_ref (a_namespace: UNICODE_STRING): UNICODE_STRING
  306. local
  307. i: INTEGER; ns: BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING]
  308. do
  309. from
  310. i := namespaces.upper
  311. until
  312. Result /= Void or else i < namespaces.lower
  313. loop
  314. ns := namespaces.item(i)
  315. if ns.has_value(a_namespace) then
  316. Result := ns.key_at(a_namespace)
  317. end
  318. i := i - 1
  319. end
  320. end
  321. attributes_for_new_node: BOOLEAN
  322. feature {} -- Memory management
  323. unused_namespaces: FAST_ARRAY[WEAK_REFERENCE[BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING]]]
  324. once
  325. create Result.make(0)
  326. end
  327. new_namespaces: BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING]
  328. local
  329. i: INTEGER
  330. do
  331. from
  332. i := unused_namespaces.upper
  333. until
  334. Result /= Void or else i < unused_namespaces.lower
  335. loop
  336. Result := unused_namespaces.item(i).item
  337. if Result /= Void then
  338. unused_namespaces.item(i).set_item(Void)
  339. Result.clear_count
  340. end
  341. i := i - 1
  342. end
  343. if Result = Void then
  344. create {HASHED_BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING]} Result.make
  345. end
  346. ensure
  347. Result.is_empty
  348. end
  349. old_namespaces (a_namespaces: BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING])
  350. local
  351. i: INTEGER; done: BOOLEAN; wr: WEAK_REFERENCE[BIJECTIVE_DICTIONARY[UNICODE_STRING, UNICODE_STRING]]; k, v: UNICODE_STRING
  352. do
  353. from
  354. i := a_namespaces.lower
  355. until
  356. a_namespaces.is_empty
  357. loop
  358. k := a_namespaces.key(i)
  359. v := a_namespaces.item(i)
  360. a_namespaces.remove(k)
  361. string_recycle(k)
  362. string_recycle(v)
  363. end
  364. from
  365. i := unused_namespaces.lower
  366. until
  367. done or else i > unused_namespaces.upper
  368. loop
  369. wr := unused_namespaces.item(i)
  370. if wr.item = Void then
  371. wr.set_item(a_namespaces)
  372. done := True
  373. end
  374. i := i + 1
  375. end
  376. if not done then
  377. create wr.set_item(a_namespaces)
  378. unused_namespaces.add_last(wr)
  379. end
  380. end
  381. string_pool: RECYCLING_POOL[UNICODE_STRING]
  382. once
  383. create Result.make
  384. end
  385. string_twin (unicode: UNICODE_STRING): UNICODE_STRING
  386. require
  387. unicode /= Void
  388. do
  389. if string_pool.is_empty then
  390. Result := unicode.twin
  391. else
  392. Result := string_pool.item
  393. Result.copy(unicode)
  394. end
  395. ensure
  396. Result.is_equal(unicode)
  397. end
  398. string_recycle (unicode: UNICODE_STRING)
  399. do
  400. string_pool.recycle(unicode)
  401. end
  402. end -- class XMLNS_PARSER
  403. --
  404. -- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
  405. --
  406. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  407. -- of this software and associated documentation files (the "Software"), to deal
  408. -- in the Software without restriction, including without limitation the rights
  409. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  410. -- copies of the Software, and to permit persons to whom the Software is
  411. -- furnished to do so, subject to the following conditions:
  412. --
  413. -- The above copyright notice and this permission notice shall be included in
  414. -- all copies or substantial portions of the Software.
  415. --
  416. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  417. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  418. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  419. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  420. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  421. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  422. -- THE SOFTWARE.