PageRenderTime 13ms CodeModel.GetById 3ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

/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--
  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.