PageRenderTime 29ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/library/xslt/src/style/xm_xslt_template.e

http://github.com/gobo-eiffel/gobo
Specman e | 610 lines | 521 code | 54 blank | 35 comment | 53 complexity | fbd596322cf66b5cecfd1d02e62920c9 MD5 | raw file
  1. note
  2. description:
  3. "xsl:template element nodes"
  4. library: "Gobo Eiffel XSLT Library"
  5. copyright: "Copyright (c) 2004-2018, Colin Adams and others"
  6. license: "MIT License"
  7. date: "$Date$"
  8. revision: "$Revision$"
  9. class XM_XSLT_TEMPLATE
  10. inherit
  11. XM_XSLT_STYLE_ELEMENT
  12. redefine
  13. make_style_element, validate, returned_item_type, mark_tail_calls,
  14. may_contain_sequence_constructor, is_permitted_child, allocate_slots,
  15. is_template, as_template
  16. end
  17. XM_XSLT_PROCEDURE
  18. XM_XPATH_ROLE
  19. XM_XSLT_MODE_CONSTANTS
  20. MA_DECIMAL_CONTEXT_CONSTANTS
  21. export {NONE} all end
  22. create {XM_XSLT_NODE_FACTORY}
  23. make_style_element
  24. feature {NONE} -- Initialization
  25. make_style_element (an_error_listener: XM_XSLT_ERROR_LISTENER; a_document: XM_XPATH_TREE_DOCUMENT; a_parent: detachable XM_XPATH_TREE_COMPOSITE_NODE;
  26. an_attribute_collection: detachable XM_XPATH_ATTRIBUTE_COLLECTION; a_namespace_list: detachable DS_ARRAYED_LIST [INTEGER];
  27. a_name_code: INTEGER; a_sequence_number: INTEGER; a_configuration: like configuration)
  28. -- Establish invariant.
  29. do
  30. internal_fingerprint := -1 -- Not yet calculated, or not a named template
  31. create slot_manager.make
  32. Precursor (an_error_listener, a_document, a_parent, an_attribute_collection, a_namespace_list, a_name_code, a_sequence_number, a_configuration)
  33. end
  34. feature -- Access
  35. priority: detachable MA_DECIMAL
  36. -- Priority of this template
  37. match: detachable XM_XSLT_PATTERN
  38. -- Pattern match
  39. required_type: detachable XM_XPATH_SEQUENCE_TYPE
  40. -- Required type for constructed sequence
  41. compiled_template: detachable XM_XSLT_COMPILED_TEMPLATE
  42. -- Compiled version of `Current'
  43. template_fingerprint: INTEGER
  44. -- Fingerprint of named template;
  45. -- If called before `prepare_attributes', then call `ensure_template_fingerprint' first.
  46. do
  47. Result := internal_fingerprint
  48. end
  49. redundant_named_template: BOOLEAN
  50. -- Current is a redundant named template
  51. feature -- Status report
  52. may_contain_sequence_constructor: BOOLEAN
  53. -- Is `Current' allowed to contain a sequence constructor?
  54. do
  55. Result := True
  56. end
  57. is_permitted_child (a_style_element: XM_XSLT_STYLE_ELEMENT): BOOLEAN
  58. -- Is `a_style_element' a permitted child of `Current'?
  59. do
  60. Result := a_style_element.is_param
  61. end
  62. feature -- Status setting
  63. mark_tail_calls
  64. -- Mark tail-recursive calls on templates and functions.
  65. local
  66. a_last_instruction: detachable XM_XSLT_STYLE_ELEMENT
  67. do
  68. if required_type = Void then
  69. -- Don't attempt tail call optimization if the return type needs checking
  70. a_last_instruction := last_child_instruction
  71. if a_last_instruction /= Void then
  72. a_last_instruction.mark_tail_calls
  73. end
  74. end
  75. end
  76. feature -- Element change
  77. allocate_slots (a_expression: XM_XPATH_EXPRESSION; a_slot_manager: XM_XPATH_SLOT_MANAGER)
  78. -- Allocate slots in the stack frame for local variables contained in `an_expression', which will include a match pattern.
  79. do
  80. a_expression.allocate_slots (1, a_slot_manager)
  81. check attached containing_stylesheet as l_containing_stylesheet then
  82. l_containing_stylesheet.allocate_pattern_slots (a_expression.last_slot_number)
  83. end
  84. end
  85. prepare_attributes
  86. -- Set the attribute list for the element.
  87. local
  88. a_cursor: DS_ARRAYED_LIST_CURSOR [INTEGER]
  89. a_name_code: INTEGER
  90. an_expanded_name, a_name_attribute, a_mode_attribute, a_priority_attribute, a_match_attribute, an_as_attribute: detachable STRING
  91. an_error: XM_XPATH_ERROR_VALUE
  92. do
  93. if attached attribute_collection as l_attribute_collection then
  94. from
  95. a_cursor := l_attribute_collection.name_code_cursor
  96. a_cursor.start
  97. until
  98. a_cursor.after or any_compile_errors
  99. loop
  100. a_name_code := a_cursor.item
  101. an_expanded_name := shared_name_pool.expanded_name_from_name_code (a_name_code)
  102. if STRING_.same_string (an_expanded_name, Name_attribute) then
  103. a_name_attribute := attribute_value_by_index (a_cursor.index)
  104. STRING_.left_adjust (a_name_attribute)
  105. STRING_.right_adjust (a_name_attribute)
  106. elseif STRING_.same_string (an_expanded_name, Mode_attribute) then
  107. a_mode_attribute := attribute_value_by_index (a_cursor.index)
  108. STRING_.left_adjust (a_mode_attribute)
  109. STRING_.right_adjust (a_mode_attribute)
  110. elseif STRING_.same_string (an_expanded_name, Priority_attribute) then
  111. a_priority_attribute := attribute_value_by_index (a_cursor.index)
  112. STRING_.left_adjust (a_priority_attribute)
  113. STRING_.right_adjust (a_priority_attribute)
  114. elseif STRING_.same_string (an_expanded_name, Match_attribute) then
  115. a_match_attribute := attribute_value_by_index (a_cursor.index)
  116. elseif STRING_.same_string (an_expanded_name, As_attribute) then
  117. an_as_attribute := attribute_value_by_index (a_cursor.index)
  118. else
  119. check_unknown_attribute (a_name_code)
  120. end
  121. a_cursor.forth
  122. variant
  123. l_attribute_collection.number_of_attributes + 1 - a_cursor.index
  124. end
  125. end
  126. prepare_mode_attribute (a_mode_attribute, a_match_attribute = Void)
  127. if not any_compile_errors then
  128. prepare_name_attribute (a_name_attribute)
  129. end
  130. if not any_compile_errors then
  131. prepare_priority_attribute (a_priority_attribute, a_match_attribute = Void)
  132. end
  133. if not any_compile_errors and then a_match_attribute /= Void then
  134. generate_pattern (a_match_attribute)
  135. if not any_compile_errors then
  136. match := last_generated_pattern
  137. role_identifier := STRING_.appended_string (STRING_.concat ("match='", a_match_attribute), "'")
  138. end
  139. end
  140. if not any_compile_errors and then a_match_attribute = Void and then a_name_attribute = Void then
  141. create an_error.make_from_string ("xsl:template must have a name or match attribute (or both)", Xpath_errors_uri, "XTSE0500", Static_error)
  142. report_compile_error (an_error)
  143. end
  144. if an_as_attribute /= Void then
  145. generate_sequence_type (an_as_attribute)
  146. if not any_compile_errors then
  147. required_type := last_generated_sequence_type
  148. end
  149. end
  150. attributes_prepared := True
  151. end
  152. ensure_template_fingerprint
  153. -- Ensure `template_fingerprint' returns correct result.
  154. local
  155. l_name: detachable STRING
  156. do
  157. if internal_fingerprint = -1 then
  158. l_name := attribute_value_by_name ("", Name_attribute)
  159. if l_name /= Void then
  160. STRING_.left_adjust (l_name)
  161. STRING_.right_adjust (l_name)
  162. if is_qname (l_name) then
  163. generate_name_code (l_name)
  164. internal_fingerprint := fingerprint_from_name_code (last_generated_name_code)
  165. if internal_fingerprint = -1 then
  166. check attached name_code_error_value as l_name_code_error_value then
  167. report_compile_error (l_name_code_error_value)
  168. end
  169. end
  170. else
  171. report_compile_error (create {XM_XPATH_ERROR_VALUE}.make_from_string (
  172. STRING_.concat (l_name, " is not a valid lexical QName"), Xpath_errors_uri, "XTSE0280", Static_error))
  173. end
  174. end
  175. end
  176. end
  177. set_redundant_named_template
  178. -- Mark as a redundant named template.
  179. do
  180. redundant_named_template := True
  181. ensure
  182. redundant_named_template_set: redundant_named_template = True
  183. end
  184. validate
  185. -- Check that the stylesheet element is valid.
  186. -- This is called once for each element, after the entire tree has been built.
  187. -- As well as validation, it can perform first-time initialisation.
  188. local
  189. l_has_required_parameters: BOOLEAN
  190. l_child_iterator: XM_XPATH_SEQUENCE_ITERATOR [XM_XPATH_NODE]
  191. do
  192. check_top_level (Void)
  193. if attached match as l_match then
  194. type_check_pattern ("match", l_match)
  195. end
  196. mark_tail_calls
  197. validated := True
  198. create compiled_template.make -- so `{XM_XSLT_CALL_TEMPLATE}.compile' can forward-referenece to it
  199. from
  200. l_child_iterator := new_axis_iterator (Child_axis)
  201. l_child_iterator.start
  202. until
  203. l_child_iterator.after
  204. loop
  205. if attached {XM_XSLT_STYLE_ELEMENT} l_child_iterator.item as l_style_element and then l_style_element.is_param
  206. and then l_style_element.as_param.is_required_parameter then
  207. l_has_required_parameters := True
  208. end
  209. l_child_iterator.forth
  210. end
  211. check attached compiled_template as l_compiled_template then
  212. l_compiled_template.set_has_required_parameters (l_has_required_parameters)
  213. end
  214. end
  215. compile (an_executable: XM_XSLT_EXECUTABLE)
  216. -- Compile `Current' to an excutable instruction.
  217. local
  218. l_rule_manager: XM_XSLT_RULE_MANAGER
  219. l_cursor: DS_ARRAYED_LIST_CURSOR [INTEGER]
  220. l_mode: XM_XSLT_MODE
  221. l_name_code: INTEGER
  222. l_rule_value: XM_XSLT_RULE_VALUE
  223. l_content: XM_XPATH_EXPRESSION
  224. l_type_checker: XM_XPATH_TYPE_CHECKER
  225. l_role: XM_XPATH_ROLE_LOCATOR
  226. l_trace_wrapper: XM_XSLT_TRACE_INSTRUCTION
  227. l_context_item_type: XM_XPATH_ITEM_TYPE
  228. l_replacement: DS_CELL [detachable XM_XPATH_EXPRESSION]
  229. do
  230. compile_sequence_constructor (an_executable, new_axis_iterator (Child_axis), True)
  231. if not attached last_generated_expression as l_last_generated_expression then
  232. create {XM_XPATH_EMPTY_SEQUENCE} l_content.make
  233. else
  234. l_content := l_last_generated_expression
  235. end
  236. if not l_content.is_error then
  237. create l_replacement.make (Void)
  238. l_content.simplify (l_replacement)
  239. check postcondition_of_simplify: attached l_replacement.item as l_replacement_item then
  240. l_content := l_replacement_item
  241. end
  242. end
  243. if attached l_content.error_value as l_error_value then
  244. check is_error: l_content.is_error end
  245. report_compile_error (l_error_value)
  246. else
  247. if attached required_type as l_required_type then
  248. check attached role_identifier as l_role_identifier then
  249. create l_role.make (Template_result_role, l_role_identifier, 1, Xpath_errors_uri, "XTTE0505")
  250. end
  251. create l_type_checker
  252. l_type_checker.static_type_check (static_context, l_content, l_required_type, False, l_role)
  253. if l_type_checker.is_static_type_check_error then
  254. check postcondition_of_static_type_check: attached l_type_checker.static_type_check_error as l_type_checker_static_type_check_error then
  255. report_compile_error (l_type_checker_static_type_check_error)
  256. end
  257. else
  258. check postcondition_of_static_type_check: attached l_type_checker.checked_expression as l_type_checker_checked_expression then
  259. l_content := l_type_checker_checked_expression
  260. end
  261. end
  262. end
  263. end
  264. if not any_compile_errors then
  265. l_context_item_type := any_item
  266. if template_fingerprint = -1 then
  267. -- Template can't be called by name, so the context item will match the match pattern
  268. check attached match as l_match then
  269. l_context_item_type := l_match.node_test
  270. end
  271. end
  272. create l_replacement.make (Void)
  273. check attached static_context as l_static_context then
  274. l_content.check_static_type (l_replacement, l_static_context, l_context_item_type)
  275. end
  276. check postcondition_of_check_static_type: attached l_replacement.item as l_replacement_item then
  277. l_content := l_replacement_item
  278. end
  279. if attached l_content.error_value as l_error_value then
  280. check is_error: l_content.is_error end
  281. report_compile_error (l_error_value)
  282. else
  283. l_replacement.put (Void)
  284. check attached static_context as l_static_context then
  285. l_content.optimize (l_replacement, l_static_context, l_context_item_type)
  286. end
  287. check postcondition_of_optimize: attached l_replacement.item as l_replacement_item then
  288. l_content := l_replacement_item
  289. end
  290. if configuration.is_tracing and not l_content.is_trace_wrapper then
  291. create l_trace_wrapper.make (l_content, an_executable, Current)
  292. check attached principal_stylesheet as l_principal_stylesheet then
  293. l_trace_wrapper.set_source_location (l_principal_stylesheet.module_number (system_id), line_number)
  294. end
  295. -- TODO: sort out - l_trace_wrapper.set_parent (compiled_template)
  296. l_content := l_trace_wrapper
  297. end
  298. check attached compiled_template as l_compiled_template then
  299. l_compiled_template.initialize (an_executable, l_content, template_fingerprint, precedence, minimum_import_precedence, system_id, line_number, slot_manager)
  300. end
  301. if attached l_content.error_value as l_error_value then
  302. check is_error: l_content.is_error end
  303. report_compile_error (l_error_value)
  304. else
  305. style_element_allocate_slots (l_content, slot_manager)
  306. if match /= Void then
  307. check attached principal_stylesheet as l_principal_stylesheet then
  308. l_rule_manager := l_principal_stylesheet.rule_manager
  309. end
  310. check attached mode_name_codes as l_mode_name_codes then
  311. from
  312. l_cursor := l_mode_name_codes.new_cursor
  313. l_cursor.start
  314. until
  315. l_cursor.after
  316. loop
  317. l_name_code := l_cursor.item
  318. if not l_rule_manager.is_mode_registered (l_name_code) then
  319. l_rule_manager.register_mode (l_name_code)
  320. end
  321. l_mode := l_rule_manager.mode (l_name_code)
  322. check attached compiled_template as l_compiled_template then
  323. create l_rule_value.make (l_compiled_template)
  324. end
  325. check attached match as l_match then
  326. if is_priority_specified then
  327. check attached priority as l_priority then
  328. l_rule_manager.set_handler (l_match, l_rule_value, l_mode, precedence, l_priority)
  329. end
  330. else
  331. l_rule_manager.set_handler_with_default_priority (l_match, l_rule_value, l_mode, precedence)
  332. end
  333. end
  334. l_cursor.forth
  335. variant
  336. l_mode_name_codes.count + 1 - l_cursor.index
  337. end
  338. end
  339. end
  340. check attached principal_stylesheet as l_principal_stylesheet then
  341. if is_explaining or else l_principal_stylesheet.is_all_explaining then
  342. std.error.put_string ("Compiled template ")
  343. if template_fingerprint /= -1 then
  344. std.error.put_string (" name=")
  345. std.error.put_string (shared_name_pool.display_name_from_name_code (template_fingerprint))
  346. end
  347. if attached match as l_match then
  348. std.error.put_string (" match=")
  349. std.error.put_string (l_match.original_text)
  350. end
  351. std.error.put_new_line
  352. -- TODO - add mode names
  353. std.error.put_string (" Optimized template body:%N")
  354. check attached compiled_template as l_compiled_template and then attached l_compiled_template.body as l_compiled_template_body then
  355. l_compiled_template_body.display (2)
  356. end
  357. end
  358. end
  359. end
  360. end
  361. end
  362. last_generated_expression := Void
  363. end
  364. feature {XM_XSLT_STYLE_ELEMENT} -- Restricted
  365. returned_item_type: XM_XPATH_ITEM_TYPE
  366. -- Type of item returned by this instruction
  367. do
  368. if not attached required_type as l_required_type then
  369. Result := common_child_item_type
  370. else
  371. Result := l_required_type.primary_type
  372. end
  373. end
  374. feature -- Conversion
  375. is_template: BOOLEAN
  376. -- Is `Current' an xsl:template?
  377. do
  378. Result := True
  379. end
  380. as_template: XM_XSLT_TEMPLATE
  381. -- `Current' seen as an xsl:template
  382. do
  383. Result := Current
  384. end
  385. feature {NONE} -- Implementation
  386. internal_fingerprint: INTEGER
  387. -- Used by `template_fingerprint'
  388. mode_name_codes: detachable DS_ARRAYED_LIST [INTEGER]
  389. -- Name codes for the modes applicable to this template
  390. is_priority_specified: BOOLEAN
  391. -- has the priority been specified?
  392. role_identifier: detachable STRING
  393. -- Role identificaton
  394. minimum_import_precedence: INTEGER
  395. -- Lowest import pecedence
  396. do
  397. check
  398. stylesheet: attached {XM_XSLT_STYLESHEET} document_element as a_stylesheet
  399. then
  400. Result := a_stylesheet.minimum_import_precedence
  401. end
  402. end
  403. prepare_mode_attribute (l_mode_attribute: detachable STRING; is_match_attribute_void: BOOLEAN)
  404. -- Prepare mode attribute
  405. require
  406. attributes_not_prepared: not attributes_prepared
  407. mode_name_codes_void: mode_name_codes = Void
  408. local
  409. a_splitter: ST_SPLITTER
  410. mode_tokens: DS_LIST [STRING]
  411. l_cursor: DS_LIST_CURSOR [STRING]
  412. l_mode: STRING
  413. an_error: XM_XPATH_ERROR_VALUE
  414. l_mode_name_codes: like mode_name_codes
  415. do
  416. if l_mode_attribute = Void then
  417. create l_mode_name_codes.make (1)
  418. l_mode_name_codes.put_last (Default_mode)
  419. mode_name_codes := l_mode_name_codes
  420. else
  421. if is_match_attribute_void then
  422. create an_error.make_from_string ("The mode attribute must be absent if the match attribute is absent", Xpath_errors_uri, "XTSE0500", Static_error)
  423. report_compile_error (an_error)
  424. else
  425. create a_splitter.make
  426. mode_tokens := a_splitter.split (l_mode_attribute)
  427. if mode_tokens.count = 0 then
  428. create an_error.make_from_string ("The mode attribute must not be empty", Xpath_errors_uri, "XTSE0550", Static_error)
  429. report_compile_error (an_error)
  430. else
  431. create l_mode_name_codes.make (mode_tokens.count)
  432. mode_name_codes := l_mode_name_codes
  433. from
  434. l_cursor := mode_tokens.new_cursor
  435. l_cursor.start
  436. until
  437. l_cursor.after
  438. loop
  439. l_mode := l_cursor.item
  440. if STRING_.same_string (l_mode, "#default") then
  441. l_mode_name_codes.put_last (Default_mode)
  442. elseif STRING_.same_string (l_mode, "#all") then
  443. if mode_tokens.count /= 1 then
  444. create an_error.make_from_string ("mode='#all' cannot be combined with other modes", Xpath_errors_uri, "XTSE0550", Static_error)
  445. report_compile_error (an_error)
  446. else
  447. l_mode_name_codes.put_last (All_modes)
  448. end
  449. elseif not is_qname (l_mode) then
  450. create an_error.make_from_string ("Mode names must be QNames or the token '#default' or the token '#all'", Xpath_errors_uri, "XTSE0550", Static_error)
  451. report_compile_error (an_error)
  452. else
  453. generate_name_code (l_mode)
  454. if last_generated_name_code = -1 then
  455. check postcondition_of_generate_name_code: attached name_code_error_value as l_name_code_error_value then
  456. report_compile_error (l_name_code_error_value)
  457. end
  458. l_cursor.go_after
  459. else
  460. l_mode_name_codes.put_last (last_generated_name_code)
  461. end
  462. end
  463. l_cursor.forth
  464. variant
  465. mode_tokens.count + 1 - l_cursor.index
  466. end
  467. if not any_compile_errors then
  468. check_all_modes_distinct
  469. end
  470. end
  471. end
  472. end
  473. ensure
  474. mode_name_codes_not_void: not any_compile_errors implies mode_name_codes /= Void
  475. end
  476. prepare_name_attribute (a_name_attribute: detachable STRING)
  477. -- Prepare name attribute
  478. local
  479. an_error: XM_XPATH_ERROR_VALUE
  480. do
  481. if a_name_attribute /= Void then
  482. if is_qname (a_name_attribute) then
  483. generate_name_code (a_name_attribute)
  484. if last_generated_name_code = -1 then
  485. check postcondition_of_generate_name_code: attached name_code_error_value as l_name_code_error_value then
  486. report_compile_error (l_name_code_error_value)
  487. end
  488. else
  489. internal_fingerprint := fingerprint_from_name_code (last_generated_name_code)
  490. end
  491. role_identifier := a_name_attribute
  492. else
  493. create an_error.make_from_string ("Template 'name' attribute must be a QName", Xpath_errors_uri, "XTSE0280", Static_error)
  494. report_compile_error (an_error)
  495. end
  496. end
  497. end
  498. prepare_priority_attribute (a_priority_attribute: detachable STRING; is_match_attribute_void: BOOLEAN)
  499. -- Prepare priority attribute
  500. local
  501. l_message: STRING
  502. l_decimal_parser: MA_DECIMAL_TEXT_PARSER
  503. l_ctx: MA_DECIMAL_CONTEXT
  504. do
  505. if a_priority_attribute /= Void then
  506. is_priority_specified := True
  507. if is_match_attribute_void then
  508. report_compile_error (create {XM_XPATH_ERROR_VALUE}.make_from_string ("The priority attribute must be absent if the match attribute is absent",
  509. Xpath_errors_uri, "XTSE0500", Static_error))
  510. else
  511. create l_decimal_parser.make
  512. create l_ctx.make (a_priority_attribute.count, round_half_up)
  513. l_decimal_parser.parse_ctx (a_priority_attribute, l_ctx, False)
  514. if not l_decimal_parser.error and a_priority_attribute.index_of ('e', 1) = 0 and a_priority_attribute.index_of ('E', 1) = 0 then
  515. check postcondition_of_parse_ctx: attached l_decimal_parser.last_decimal as l_last_decimal then
  516. priority := l_last_decimal
  517. end
  518. else
  519. l_message := STRING_.appended_string ("Invalid decimal value for priority (", a_priority_attribute)
  520. l_message := STRING_.appended_string (l_message, ")")
  521. report_compile_error (create {XM_XPATH_ERROR_VALUE}.make_from_string (l_message, Xpath_errors_uri, "XTSE0530", Static_error))
  522. end
  523. end
  524. end
  525. end
  526. check_all_modes_distinct
  527. -- Check no duplicate mode names.
  528. local
  529. a_set: DS_HASH_SET [INTEGER]
  530. l_cursor: DS_ARRAYED_LIST_CURSOR [INTEGER]
  531. l_name_code: INTEGER
  532. an_error: XM_XPATH_ERROR_VALUE
  533. do
  534. check attached mode_name_codes as l_mode_name_codes then
  535. create a_set.make (l_mode_name_codes.count)
  536. from
  537. l_cursor := l_mode_name_codes.new_cursor; l_cursor.start
  538. until
  539. any_compile_errors or else l_cursor.after
  540. loop
  541. l_name_code := l_cursor.item
  542. if a_set.has (l_name_code) then
  543. create an_error.make_from_string ("Mode names must all be distinct", Xpath_errors_uri, "XTSE0550", Static_error)
  544. report_compile_error (an_error)
  545. else
  546. a_set.put (l_name_code)
  547. end
  548. l_cursor.forth
  549. variant
  550. l_mode_name_codes.count + 1 - l_cursor.index
  551. end
  552. end
  553. end
  554. invariant
  555. mode_name_codes_not_void: attributes_prepared and then not any_compile_errors implies mode_name_codes /= Void
  556. end