class EL_REFLECTED_COLLECTION

(source code)

Client examples: REFLECTION_TEST_SET

description

Reflected field conforming to COLLECTION [G] with ability to create factories for types conforming to ARRAYED_LIST [G] or HASH_TABLE [G, HASHABLE]

notes

Factory types returned by new_factory

EL_FACTORY [G]*
   EL_ARRAYED_LIST_FACTORY [G -> ARRAYED_LIST [ANY] create make end]
   EL_HASH_TABLE_FACTORY [G -> HASH_TABLE [ANY, HASHABLE] create make, make_equal end]
note
	description: "[
		Reflected field conforming to ${COLLECTION [G]} with ability to create factories for types
		conforming to ${ARRAYED_LIST [G]} or ${HASH_TABLE [G, HASHABLE]}
	]"
	notes: "[
		Factory types returned by `new_factory'
		
			EL_FACTORY [G]*
				${EL_ARRAYED_LIST_FACTORY [G -> ARRAYED_LIST [ANY] create make end]}
				${EL_HASH_TABLE_FACTORY [G -> HASH_TABLE [ANY, HASHABLE] create make, make_equal end]}
	]"

	author: "Finnian Reilly"
	copyright: "Copyright (c) 2001-2022 Finnian Reilly"
	contact: "finnian at eiffel hyphen loop dot com"

	license: "MIT license (See: en.wikipedia.org/wiki/MIT_License)"
	date: "2024-11-06 10:51:00 GMT (Wednesday 6th November 2024)"
	revision: "41"

class
	EL_REFLECTED_COLLECTION [G]

inherit
	EL_REFLECTED_REFERENCE [COLLECTION [G]]
		rename
			value as collection
		redefine
			append_to_string, group_type, post_make, is_abstract, is_storable_type, new_factory,
			set_from_memory, set_from_string, to_string, write
		end

	EL_MODULE_CONVERT_STRING

	EL_STRING_8_CONSTANTS

	EL_SHARED_CLASS_ID; EL_SHARED_NEW_INSTANCE_TABLE; EL_SHARED_ZSTRING_BUFFER_POOL

create
	make, default_create -- to satisfy constraint of `EL_REFLECTED_COLLECTION_FACTORY'

feature {NONE} -- Initialization

	post_make
		-- initialization after types have been set
		require else
			is_string_convertible: Convert_string.has (({G}).type_id)
		do
			item_type_id := ({G}).type_id
			Precursor

		-- Check if there is a function to make new collection items that has a target which is
		-- is the same type as the current object type
			if New_instance_table.has_key (item_type_id)
				and then attached {FUNCTION [G]} New_instance_table.found_item as l_new_item_function
				and then {ISE_RUNTIME}.dynamic_type (l_new_item_function.target) = object_type
			then
				new_item_function := l_new_item_function
			end

			if Item_reader_writer_table.has_key (item_type_id)
				and then attached {EL_READER_WRITER_INTERFACE [G]} Item_reader_writer_table.found_item as found_item
			then
				reader_writer := found_item

			elseif attached makeable_reader_writer_factory as reader_writer_factory
				and then attached {EL_READER_WRITER_INTERFACE [G]} reader_writer_factory.new_item as new
			then
				reader_writer := new
				Item_reader_writer_table.extend (new, item_type_id)
			end
		ensure then
			valid_reader_writer: attached reader_writer as rw implies rw.item_type ~ {G}
		end

feature -- Access

	item_type_id: INTEGER

	group_type: TYPE [ANY]
		do
			Result := {COLLECTION [ANY]}
		end

feature -- Conversion

	to_string (a_object: EL_REFLECTIVE): ZSTRING
		local
			list: EL_ZSTRING_LIST
		do
			create list.make_from_general (to_string_list (a_object))
			Result := list.comma_separated
		end

feature -- Status query

	has_character_data: BOOLEAN
		do
			Result := Eiffel.is_type_in_set (item_type_id, Class_id.Character_data_types)
		end

	is_extendible (a_object: EL_REFLECTIVE): BOOLEAN
		do
			Result := attached collection (a_object) and then attached reader_writer
		end

	is_reflective_item: BOOLEAN
		do
			Result := {ISE_RUNTIME}.type_conforms_to (item_type_id, Class_id.EL_REFLECTIVE)
		end

feature -- Basic operations

	append_to_string (a_object: EL_REFLECTIVE; str: ZSTRING)
		local
			i: INTEGER
		do
			if attached {ITERABLE [G]} collection (a_object) as reflective_list then
				across reflective_list as list loop
					i := i + 1
					if i > 1 then
						str.append_string_general (Comma_space)
					end
					if attached reader_writer as writer then
						writer.write (list.item, str)
					end
				end
			end
		end

	extend_with_new (a_object: EL_REFLECTIVE)
		local
			new_item: G
		do
			if attached collection (a_object) as container then
				if attached new_item_function as function then
					function.set_target (a_object)
					function.apply
					new_item  := function.last_result

				elseif attached reader_writer as rw  then
					new_item := rw.new_item
				end
				container.extend (new_item)
			end
		end

	extend_from_readable (a_object: EL_REFLECTIVE; readable: EL_READABLE)
		do
			if attached reader_writer as reader then
				collection (a_object).extend (reader.read_item (readable))

			elseif attached {G} Convert_string.to_type_of_type (readable.read_string, item_type_id) as new then
				collection (a_object).extend (new)
			end
		end

	print_items (a_object: EL_REFLECTIVE; a_lio: EL_LOGGABLE)
		require
			reflective_item: is_reflective_item
		local
			i: INTEGER
		do
			if attached {ITERABLE [EL_REFLECTIVE]} collection (a_object) as reflective_list then
				across reflective_list as list loop
					i := i + 1
					a_lio.put_labeled_substitution (name, "[%S]", [i])
					a_lio.tab_right
					a_lio.put_new_line
					list.item.print_fields (a_lio)
					a_lio.tab_left
					a_lio.put_new_line
				end
			end
		end

	set_from_memory (a_object: EL_REFLECTIVE; memory: EL_MEMORY_READER_WRITER)
		local
			item_count, i: INTEGER
		do
			if attached reader_writer as reader
				and then attached {CHAIN [G]} collection (a_object) as item_list
			then
				item_count := memory.read_integer_32
				if attached {ARRAYED_LIST [G]} item_list as array then
					array.grow (item_count)
				end
				from i := 1 until i > item_count loop
					item_list.extend (reader.read_item (memory))
					i := i + 1
				end
			end
		end

	set_from_string (a_object: EL_REFLECTIVE; csv_string: READABLE_STRING_GENERAL)
		-- if collection conforms to type `CHAIN [G]' when {G} is character data type
		-- then fill with data from comma separated `csv_string' using left adjusted values
		do
			if attached {CHAIN [ANY]} collection (a_object) as chain then
				if Convert_string.is_convertible_list (item_type_id, csv_string, ',', True) then
					chain.wipe_out
					Convert_string.append_to_chain (item_type_id, chain, csv_string, True)
				else
					check
						convertable_string: False
					end
				end
			end
		end

	write (a_object: EL_REFLECTIVE; writable: EL_WRITABLE)
		do
			if attached reader_writer as writer
				and then attached {FINITE [G]} collection (a_object) as finite
				and then attached finite.linear_representation as item_list
			then
				writable.write_integer_32 (finite.count)
				item_list.do_all (agent writer.write (?, writable))
			end
		end

feature -- Conversion

	to_string_list (a_object: EL_REFLECTIVE): EL_ARRAYED_LIST [READABLE_STRING_GENERAL]
		local
			intermediate: EL_ARRAYED_RESULT_LIST [G, READABLE_STRING_GENERAL]
		do
			if attached collection (a_object) as container then
				create intermediate.make (container, agent to_item_string)
				create Result.make_from_special (intermediate.area)
			else
				create Result.make_empty
			end
		end

feature {NONE} -- Implementation

	makeable_reader_writer_factory: detachable like Makeable_reader_writer_factory_factory.new_item_factory
		do
			if {ISE_RUNTIME}.type_conforms_to (item_type_id, Class_id.EL_MAKEABLE) then
				Result := Makeable_reader_writer_factory_factory.new_item_factory (item_type_id)
			end
		end

	new_factory: detachable EL_FACTORY [COLLECTION [G]]
		-- create factory for types conforming to `ARRAYED_LIST [ANY]' or `HASH_TABLE [ANY, HASHABLE]'
		local
			factory_any: detachable EL_FACTORY [ANY]
		do
			if Eiffel.field_conforms_to (type_id, Class_id.ARRAYED_LIST__ANY) then
				factory_any := Arrayed_list_factory.new_item_factory (type_id)

			elseif Eiffel.field_conforms_to (type_id, Class_id.HASH_TABLE__ANY__HASHABLE) then
				factory_any := Hash_table_factory.new_item_factory (type_id)
			end
			if attached {EL_FACTORY [COLLECTION [G]]} factory_any as factory_collection then
				Result := factory_collection
			else
				Result := Precursor
			end
		end

	reader_writer_types: TUPLE [
		EL_BOOLEAN_READER_WRITER,

		EL_CHARACTER_8_READER_WRITER, EL_CHARACTER_32_READER_WRITER,

		EL_INTEGER_8_READER_WRITER, EL_INTEGER_16_READER_WRITER,
		EL_INTEGER_32_READER_WRITER, EL_INTEGER_64_READER_WRITER,

		EL_NATURAL_8_READER_WRITER, EL_NATURAL_16_READER_WRITER,
		EL_NATURAL_32_READER_WRITER, EL_NATURAL_64_READER_WRITER,

		EL_REAL_32_READER_WRITER, EL_REAL_64_READER_WRITER,

		EL_STRING_8_READER_WRITER, EL_STRING_32_READER_WRITER,
		EL_ZSTRING_READER_WRITER
	]
		do
			create Result
		end

	to_item_string (item: G): READABLE_STRING_GENERAL
		do
			if attached {READABLE_STRING_GENERAL} item as str then
				Result := str

			elseif attached {EL_PATH} item as path then
				Result := path.to_string

			elseif attached reader_writer as writer and then attached String_pool.borrowed_item as borrowed then
				if attached borrowed.empty as str then
					writer.write (item, str)
					Result := str.twin
				end
				borrowed.return
			else
				Result := item.out
			end
		end

feature {EL_REFLECTION_HANDLER} -- Internal attributes

	reader_writer: detachable EL_READER_WRITER_INTERFACE [G]
		-- item reader/writer

	new_item_function: detachable FUNCTION [G]
		-- functon that returns a new item from agent defined in `enclosing_object' type
		-- (Example in class `FTP_BACKUP_COMMAND')

feature {NONE} -- Constants

	Is_abstract: BOOLEAN = True
		-- `True' if field type is deferred

	Is_storable_type: BOOLEAN = False
		-- is type storable using `EL_STORABLE' interface

	Item_reader_writer_table: EL_HASH_TABLE [EL_READER_WRITER_INTERFACE [ANY], INTEGER]
		local
			type_list: EL_TUPLE_TYPE_LIST [EL_READER_WRITER_INTERFACE [ANY]]
		once
			create type_list.make_from_tuple (reader_writer_types)
			create Result.make (type_list.count)
			across type_list as list loop
				if attached {EL_READER_WRITER_INTERFACE [ANY]} Eiffel.new_object (list.item) as new then
					Result.extend (new, new.item_type.type_id)
				end
			end
			-- Might also handle `COLLECTION [INTEGER_X]' from encryption.ecf for example
			Result.merge (Reader_writer_table)
		end

end