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