/src/lib/storage/internal/repository_impl.e

http://github.com/tybor/Liberty · Specman e · 674 lines · 562 code · 53 blank · 59 comment · 25 complexity · 8872ab0215b56e63c8dbd4e2adab0587 MD5 · raw file

  1. -- This file is part of a Liberty Eiffel library.
  2. -- See the full copyright at the end.
  3. --
  4. deferred class REPOSITORY_IMPL[O_ -> STORABLE]
  5. --
  6. -- Used to implement update and commit. Takes care of object references and cycles.
  7. --
  8. -- Update is usually event-driven; here are only tools to correctly create the objects layout (see
  9. -- `read_from_stream' and `update_from_stream').
  10. --
  11. -- Commit is, on the other hand, a method template with a few deferred methods (see `write_to_stream').
  12. --
  13. -- Vocabulary:
  14. --
  15. -- * layout: the description of the contents of an object (its layout). This object is referenced
  16. -- by a reference (see below).
  17. --
  18. -- * reference: a reference to an object. Note that the layout of the object MUST have been defined before
  19. -- any reference links to it. A special 'Void' ref indicates a Void object.
  20. --
  21. -- * embedded: a user-defined expanded object (i.e. not one of the basic objects)
  22. --
  23. -- * basic: a special expanded object, with a simple value, specially treated by the compiler and by this
  24. -- class. Basic types are INTEGER and co., READ and co., CHARACTER and BOOLEAN.
  25. --
  26. -- * array: a NATIVE_ARRAY of objects. The type is, in that particular case, the type of the items.
  27. --
  28. -- See also XML_REPOSITORY_IMPL
  29. --
  30. inherit
  31. REPOSITORY[O_]
  32. insert
  33. INTERNALS_HANDLER
  34. feature {ANY} -- Error handling on repository update
  35. register_update_error_handler (a_error_handler: PROCEDURE[TUPLE[ABSTRACT_STRING, INTEGER, INTEGER]])
  36. do
  37. update_error_handlers.add_last(a_error_handler)
  38. end
  39. feature {}
  40. update_error_handlers: FAST_ARRAY[PROCEDURE[TUPLE[ABSTRACT_STRING, INTEGER, INTEGER]]]
  41. fire_update_error (message: ABSTRACT_STRING; line, column: INTEGER)
  42. local
  43. i: INTEGER
  44. do
  45. breakpoint
  46. from
  47. i := update_error_handlers.lower
  48. until
  49. i > update_error_handlers.upper
  50. loop
  51. update_error_handlers.item(i).call([message, line, column])
  52. i := i + 1
  53. end
  54. end
  55. feature {} -- Implementation of update
  56. solve_again: BOOLEAN
  57. update_layouts: STACK[REPOSITORY_LAYOUT]
  58. once
  59. create Result.make
  60. end
  61. updated_internals: AVL_DICTIONARY[INTERNALS, INTEGER]
  62. once
  63. create Result.make
  64. end
  65. internals_references: HASHED_DICTIONARY[INTEGER, POINTER]
  66. once
  67. create Result.make
  68. end
  69. layouts: FAST_ARRAY[REPOSITORY_LAYOUT]
  70. once
  71. create Result.make(0)
  72. end
  73. objects: AVL_DICTIONARY[INTEGER, STRING]
  74. once
  75. create Result.make
  76. end
  77. solve (ref: INTEGER): INTERNALS
  78. require
  79. ref > 0
  80. do
  81. Result := updated_internals.fast_reference_at(ref)
  82. if Result = Void then
  83. solve_again := True
  84. end
  85. end
  86. internals_reference (internals: INTERNALS): INTEGER
  87. require
  88. not internals.type_is_expanded
  89. local
  90. p: POINTER
  91. do
  92. p := internals.object_as_pointer
  93. if internals_references.fast_has(p) then
  94. Result := internals_references.fast_at(p)
  95. else
  96. Result := internals_references.count + 1 -- never 0 (is Void)
  97. internals_references.add(Result, p)
  98. end
  99. end
  100. solver: FUNCTION[TUPLE[INTEGER], INTERNALS]
  101. once
  102. Result := agent solve
  103. end
  104. read_from_stream (in_stream: INPUT_STREAM)
  105. local
  106. ref: INTEGER
  107. do
  108. repository.clear_count
  109. check
  110. update_layouts.is_empty
  111. end
  112. from
  113. until
  114. updated_internals.is_empty
  115. loop
  116. ref := updated_internals.key(updated_internals.lower)
  117. updated_internals.remove(ref)
  118. end
  119. update_from_stream(in_stream)
  120. end
  121. update_from_stream (in_stream: INPUT_STREAM)
  122. do
  123. register_transient_objects
  124. do_update(in_stream)
  125. unregister_transient_objects
  126. if not update_layouts.is_empty then
  127. fire_update_error(once "Some layouts are still to be consumed", last_line, last_column)
  128. end
  129. end
  130. last_line: INTEGER
  131. deferred
  132. end
  133. last_column: INTEGER
  134. deferred
  135. end
  136. do_update (in_stream: INPUT_STREAM)
  137. deferred
  138. end
  139. record_object (ref: INTEGER; name: STRING; line, column: INTEGER)
  140. -- Register the object as a high-level one, i.e. put it in the repository.
  141. local
  142. typed: TYPED_INTERNALS[O_]; error: STRING
  143. do
  144. if not updated_internals.has(ref) then
  145. error := once ""
  146. error.copy(once "Unknown reference: ")
  147. ref.append_in(error)
  148. fire_update_error(error, line, column)
  149. else
  150. typed ::= solve(ref)
  151. put(typed.object, name)
  152. end
  153. end
  154. check_non_empty_data (a_data, data_type: STRING; line, column: INTEGER)
  155. local
  156. error: STRING
  157. do
  158. if a_data = Void or else a_data.is_empty then
  159. error := once ""
  160. error.copy(once "Invalid empty ")
  161. error.append(data_type)
  162. error.append(once ": ")
  163. error.append(a_data)
  164. fire_update_error(error, line, column)
  165. end
  166. end
  167. open_repository (a_repository: REPOSITORY_LAYOUT; line, column: INTEGER)
  168. require
  169. a_repository.kind.is_equal(once "repository")
  170. do
  171. objects.clear_count
  172. layouts.clear_count
  173. end
  174. open_layout (a_type: STRING; a_ref: INTEGER; a_layout: REPOSITORY_LAYOUT; line, column: INTEGER)
  175. require
  176. a_layout.kind.is_equal(once "layout")
  177. a_ref > 0
  178. do
  179. check_non_empty_data(a_type, once "type", line, column)
  180. --check_non_empty_data(a_ref, once "ref", line, column)
  181. a_layout.set_type(a_type)
  182. a_layout.set_ref(a_ref)
  183. end
  184. open_reference (a_name: STRING; a_ref: INTEGER; a_reference: REPOSITORY_LAYOUT; line, column: INTEGER)
  185. require
  186. a_reference.kind.is_equal(once "reference")
  187. a_ref >= 0 -- 0 is Void
  188. do
  189. check_non_empty_data(a_name, once "name", line, column)
  190. --check_non_empty_data(a_ref, once "ref", line, column)
  191. a_reference.set_name(a_name)
  192. a_reference.set_ref(a_ref)
  193. end
  194. open_embedded (a_name, a_type: STRING; a_embedded: REPOSITORY_LAYOUT; line, column: INTEGER)
  195. require
  196. a_embedded.kind.is_equal(once "embedded")
  197. do
  198. check_non_empty_data(a_name, once "name", line, column)
  199. check_non_empty_data(a_type, once "type", line, column)
  200. a_embedded.set_name(a_name)
  201. a_embedded.set_type(a_type)
  202. end
  203. open_basic (a_name, a_type, a_value: STRING; a_basic: REPOSITORY_LAYOUT; line, column: INTEGER)
  204. require
  205. a_basic.kind.is_equal(once "basic")
  206. do
  207. check_non_empty_data(a_name, once "name", line, column)
  208. check_non_empty_data(a_type, once "type", line, column)
  209. check_non_empty_data(a_value, once "value", line, column)
  210. a_basic.set_name(a_name)
  211. a_basic.set_type(a_type)
  212. a_basic.set_value(a_value)
  213. end
  214. open_array (a_name, a_type: STRING; a_capacity: INTEGER; a_array: REPOSITORY_LAYOUT; line, column: INTEGER)
  215. require
  216. a_array.kind.is_equal(once "array")
  217. local
  218. error: STRING
  219. do
  220. check_non_empty_data(a_name, once "name", line, column)
  221. check_non_empty_data(a_type, once "type", line, column)
  222. if a_capacity < 0 then
  223. error := once ""
  224. error.copy(once "Invalid negative capacity: ")
  225. a_capacity.append_in(error)
  226. fire_update_error(error, line, column)
  227. end
  228. a_array.set_name(a_name)
  229. a_array.set_type(a_type)
  230. a_array.set_capacity(a_capacity)
  231. end
  232. close_repository (line, column: INTEGER)
  233. require
  234. update_layouts.top.kind.is_equal(once "repository")
  235. local
  236. layout: REPOSITORY_LAYOUT; internals: INTERNALS; i, c: INTEGER
  237. do
  238. update_layouts.pop
  239. check
  240. update_layouts.is_empty
  241. end
  242. from
  243. solve_again := True
  244. c := layouts.count
  245. variant
  246. c
  247. until
  248. not solve_again
  249. loop
  250. solve_again := False
  251. from
  252. i := layouts.lower
  253. until
  254. i > layouts.upper
  255. loop
  256. layout := layouts.item(i)
  257. check
  258. layout.kind.is_equal(once "layout")
  259. end
  260. internals := layout.solve(solver)
  261. if internals = Void then
  262. -- Cannot do anything, at this stage the object will never be found.
  263. -- solve_again := True
  264. elseif updated_internals.has(layout.ref) then
  265. check
  266. internals = updated_internals.at(layout.ref)
  267. end
  268. else
  269. updated_internals.add(internals, layout.ref)
  270. c := c - 1
  271. end
  272. i := i + 1
  273. end
  274. end
  275. from
  276. i := objects.lower
  277. until
  278. i > objects.upper
  279. loop
  280. record_object(objects.item(i), objects.key(i), line, column)
  281. i := i + 1
  282. end
  283. end
  284. close_layout (line, column: INTEGER)
  285. require
  286. update_layouts.top.kind.is_equal(once "layout")
  287. local
  288. layout: REPOSITORY_LAYOUT
  289. do
  290. layout := update_layouts.top
  291. update_layouts.pop
  292. layouts.add_last(layout)
  293. end
  294. close_reference (line, column: INTEGER)
  295. require
  296. update_layouts.top.kind.is_equal(once "reference")
  297. local
  298. layout: REPOSITORY_LAYOUT
  299. do
  300. layout := update_layouts.top
  301. update_layouts.pop
  302. if update_layouts.count = 1 then
  303. -- means only the "repository" node is open
  304. objects.add(layout.ref, layout.name)
  305. else
  306. update_layouts.top.add_layout(layout)
  307. end
  308. end
  309. close_embedded (line, column: INTEGER)
  310. require
  311. update_layouts.top.kind.is_equal(once "embedded")
  312. local
  313. layout: REPOSITORY_LAYOUT
  314. do
  315. layout := update_layouts.top
  316. update_layouts.pop
  317. update_layouts.top.add_layout(layout)
  318. end
  319. close_basic (line, column: INTEGER)
  320. require
  321. update_layouts.top.kind.is_equal(once "basic")
  322. local
  323. layout: REPOSITORY_LAYOUT
  324. do
  325. layout := update_layouts.top
  326. update_layouts.pop
  327. update_layouts.top.add_layout(layout)
  328. end
  329. close_array (line, column: INTEGER)
  330. require
  331. update_layouts.top.kind.is_equal(once "array")
  332. local
  333. layout: REPOSITORY_LAYOUT
  334. do
  335. layout := update_layouts.top
  336. update_layouts.pop
  337. update_layouts.top.add_layout(layout)
  338. end
  339. feature {} -- Implementation of commit
  340. commit_map: SET[POINTER]
  341. -- Used when committing object not to commit them twice
  342. once
  343. create {HASHED_SET[POINTER]} Result.make
  344. end
  345. write_to_stream (out_stream: REPOSITORY_OUTPUT)
  346. require
  347. out_stream.is_connected
  348. local
  349. i: INTEGER; o: O_
  350. do
  351. register_transient_objects
  352. commit_map.clear_count
  353. internals_references.clear_count
  354. out_stream.start_write
  355. from
  356. i := lower
  357. until
  358. i > upper
  359. loop
  360. write_object(key(i), item(i), out_stream)
  361. i := i + 1
  362. end
  363. if o = Void then -- O_ is a reference type
  364. from
  365. i := lower
  366. until
  367. i > upper
  368. loop
  369. if item(i) /= Void then
  370. write_layout(item(i).to_internals, out_stream)
  371. end
  372. i := i + 1
  373. end
  374. end
  375. out_stream.end_write
  376. unregister_transient_objects
  377. end
  378. write_object (name: like key; object: like item; out_stream: REPOSITORY_OUTPUT)
  379. local
  380. int: INTERNALS
  381. do
  382. if object /= Void then
  383. int := object.to_internals
  384. end
  385. write_internals(int, name, out_stream)
  386. end
  387. write_internals (int: INTERNALS; name: STRING; out_stream: REPOSITORY_OUTPUT)
  388. do
  389. if int /= Void and then int.type_is_expanded then
  390. write_expanded(int, name, out_stream)
  391. else
  392. write_reference_layout(int, name, out_stream)
  393. end
  394. end
  395. write_reference_layout (reference: INTERNALS; name: STRING; out_stream: REPOSITORY_OUTPUT)
  396. require
  397. reference /= Void implies not reference.type_is_expanded
  398. local
  399. ref: INTEGER; t_ref: STRING
  400. do
  401. if reference = Void then
  402. out_stream.write_reference(0, name)
  403. else
  404. t_ref := transient.reference(reference)
  405. if t_ref /= Void then
  406. out_stream.write_transient_reference(t_ref, name)
  407. else
  408. ref := internals_reference(reference)
  409. out_stream.write_reference(ref, name)
  410. end
  411. end
  412. end
  413. write_layout (layout: INTERNALS; out_stream: REPOSITORY_OUTPUT)
  414. require
  415. not commit_map.has(layout.object_as_pointer)
  416. not layout.type_is_expanded
  417. local
  418. i: INTEGER; int: INTERNALS; ref: INTEGER
  419. do
  420. -- Add the pointer to the map of "known objects" (those already written). It must be done first
  421. -- because of possible recursion
  422. commit_map.add(layout.object_as_pointer)
  423. if transient.reference(layout) = Void then
  424. -- Write the nested objects not already defined
  425. from
  426. i := 1
  427. until
  428. i > layout.type_attribute_count
  429. loop
  430. int := layout.object_attribute(i)
  431. if int /= Void then
  432. if int.type_is_expanded then
  433. if int.type_is_native_array then
  434. write_array_fields_layouts(int, out_stream)
  435. end
  436. elseif not commit_map.has(int.object_as_pointer) then
  437. write_layout(int, out_stream)
  438. end
  439. end
  440. i := i + 1
  441. end
  442. ref := internals_reference(layout)
  443. out_stream.start_layout(ref, layout.type_generating_type)
  444. write_contents(layout, out_stream)
  445. out_stream.end_layout
  446. end
  447. ensure
  448. commit_map.has(layout.object_as_pointer)
  449. end
  450. write_contents (layout: INTERNALS; out_stream: REPOSITORY_OUTPUT)
  451. require
  452. layout.type_is_expanded or else transient.reference(layout) = Void
  453. not layout.type_is_native_array
  454. local
  455. i: INTEGER
  456. do
  457. from
  458. i := 1
  459. until
  460. i > layout.type_attribute_count
  461. loop
  462. write_internals(layout.object_attribute(i), layout.type_attribute_name(i), out_stream)
  463. i := i + 1
  464. end
  465. end
  466. write_array_contents (layout: INTERNALS; out_stream: REPOSITORY_OUTPUT)
  467. require
  468. layout.type_is_native_array
  469. local
  470. i: INTEGER
  471. do
  472. from
  473. i := 1
  474. until
  475. i > layout.type_attribute_count
  476. loop
  477. write_internals(layout.object_attribute(i), Void, out_stream)
  478. i := i + 1
  479. end
  480. end
  481. write_array_fields_layouts (array: INTERNALS; out_stream: REPOSITORY_OUTPUT)
  482. require
  483. array.type_is_expanded and then array.type_is_native_array
  484. local
  485. f: INTEGER; int: INTERNALS
  486. do
  487. from
  488. f := 1
  489. until
  490. f > array.type_attribute_count
  491. loop
  492. int := array.object_attribute(f)
  493. if int /= Void and then not int.type_is_expanded and then not commit_map.has(int.object_as_pointer) then
  494. write_layout(int, out_stream)
  495. end
  496. f := f + 1
  497. end
  498. end
  499. write_expanded (internals: INTERNALS; name: STRING; out_stream: REPOSITORY_OUTPUT)
  500. require
  501. internals.type_is_expanded
  502. local
  503. type: STRING
  504. do
  505. type := internals.type_generating_type
  506. inspect
  507. type
  508. when "CHARACTER" then
  509. out_stream.write_character_layout_object(internals, name)
  510. when "BOOLEAN" then
  511. out_stream.write_boolean_layout_object(internals, name)
  512. when "INTEGER_8" then
  513. out_stream.write_integer_8_layout_object(internals, name)
  514. when "INTEGER_16" then
  515. out_stream.write_integer_16_layout_object(internals, name)
  516. when "INTEGER" then
  517. out_stream.write_integer_layout_object(internals, name)
  518. when "INTEGER_32" then
  519. out_stream.write_integer_32_layout_object(internals, name)
  520. when "INTEGER_64" then
  521. out_stream.write_integer_64_layout_object(internals, name)
  522. when "REAL" then
  523. out_stream.write_real_layout_object(internals, name)
  524. when "REAL_32" then
  525. out_stream.write_real_32_layout_object(internals, name)
  526. when "REAL_64" then
  527. out_stream.write_real_64_layout_object(internals, name)
  528. when "REAL_80" then
  529. out_stream.write_real_80_layout_object(internals, name)
  530. when "REAL_128" then
  531. out_stream.write_real_128_layout_object(internals, name)
  532. when "REAL_EXTENDED" then
  533. out_stream.write_real_expanded_layout_object(internals, name)
  534. else
  535. if internals.type_is_native_array then
  536. write_array_layout_object(internals, name, out_stream)
  537. else
  538. write_embedded_layout_object(internals, name, out_stream)
  539. end
  540. end
  541. end
  542. write_array_layout_object (internals: INTERNALS; name: STRING; out_stream: REPOSITORY_OUTPUT)
  543. require
  544. internals.type_is_native_array
  545. do
  546. if internals.type_attribute_count > 0 then
  547. out_stream.start_array_layout(internals, name)
  548. write_array_contents(internals, out_stream)
  549. out_stream.end_array_layout(internals, name)
  550. end
  551. end
  552. write_embedded_layout_object (internals: INTERNALS; name: STRING; out_stream: REPOSITORY_OUTPUT)
  553. do
  554. out_stream.start_embedded_layout(internals, name)
  555. write_contents(internals, out_stream)
  556. out_stream.end_embedded_layout(internals, name)
  557. end
  558. feature {} -- Internals
  559. layouts_pool: RECYCLING_POOL[REPOSITORY_LAYOUT]
  560. once
  561. create Result.make
  562. end
  563. new_layout (a_kind: STRING): REPOSITORY_LAYOUT
  564. do
  565. if layouts_pool.is_empty then
  566. create Result.make
  567. else
  568. Result := layouts_pool.item
  569. end
  570. check
  571. Result.is_clear
  572. end
  573. Result.set_kind(a_kind)
  574. ensure
  575. Result.kind.is_equal(a_kind)
  576. end
  577. release_layout (a_layout: REPOSITORY_LAYOUT)
  578. do
  579. layouts_pool.recycle(a_layout)
  580. end
  581. transient: REPOSITORY_TRANSIENT
  582. feature {} -- Creation
  583. make
  584. -- Create a not-connected empty repository.
  585. do
  586. if repository = Void then
  587. create {LINKED_HASHED_DICTIONARY[O_, STRING]} repository.make
  588. create update_error_handlers.with_capacity(2)
  589. else
  590. repository.clear_count
  591. update_error_handlers.clear_count
  592. end
  593. end
  594. feature {} -- Transient objects
  595. register_transient_objects
  596. deferred
  597. end
  598. unregister_transient_objects
  599. deferred
  600. end
  601. end -- class REPOSITORY_IMPL
  602. --
  603. -- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
  604. --
  605. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  606. -- of this software and associated documentation files (the "Software"), to deal
  607. -- in the Software without restriction, including without limitation the rights
  608. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  609. -- copies of the Software, and to permit persons to whom the Software is
  610. -- furnished to do so, subject to the following conditions:
  611. --
  612. -- The above copyright notice and this permission notice shall be included in
  613. -- all copies or substantial portions of the Software.
  614. --
  615. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  616. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  617. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  618. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  619. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  620. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  621. -- THE SOFTWARE.