class EL_FIELD_LIST

(source code)

description

List of fields conforming to EL_REFLECTED_FIELD

note
	description: "List of fields conforming to ${EL_REFLECTED_FIELD}"

	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: "2025-03-22 12:21:43 GMT (Saturday 22nd March 2025)"
	revision: "30"

class
	EL_FIELD_LIST

inherit
	EL_ARRAYED_LIST [EL_REFLECTED_FIELD]
		rename
			make as make_list,
			readable as is_readable
		redefine
			initialize
		end

	EL_REFLECTION_HANDLER

	EL_SHARED_CYCLIC_REDUNDANCY_CHECK_32

create
	make, make_empty

feature {NONE} -- Initialization

	initialize
		do
			Precursor
			create table.make (capacity, foreign_naming)
		end

	make (meta_data: EL_CLASS_META_DATA)
		do
			if attached meta_data.field_info_table as info_table
				and then attached meta_data.target as target
				and then attached info_table.new_not_transient_subset (target.new_transient_fields) as field_names
			then
				foreign_naming := target.foreign_naming
				make_list (field_names.count)
				across field_names as list loop
					if attached list.item as name then
						if info_table.has_immutable_key (name)
							and then attached info_table.found_type_info as type_info
							and then target.field_included (type_info)
							and then attached meta_data.new_reflected_field (type_info, name) as new_field
						then
							extend (new_field)
							if attached foreign_naming as naming then
								new_field.set_export_name (naming.exported (new_field.name))
								naming.inform (new_field.name)
							end
						end
					end
				end
				set_order (target.new_field_sorter, info_table)
				fill_table (target.new_representations)
			else
				make_empty
			end
		ensure
			valid_list: is_valid
		end

feature -- Access

	field_hash: NATURAL
		-- CRC checksum for field names and field types
		local
			i: INTEGER
		do
			if attached crc_generator as crc and then attached area as field then
				from i := 0 until i = field.count loop
					field [i].write_field_hash (crc)
					i := i + 1
				end
				Result := crc.checksum
			end
		end

	field_with (object: EL_REFLECTIVE; value: ANY): detachable EL_REFLECTED_FIELD
		-- Reflected field in `object' for reference `value'. `Void' if not found.
		require
			is_reference_value: not value.generating_type.is_expanded
		local
			type_id, i: INTEGER
		do
			type_id := {ISE_RUNTIME}.dynamic_type (value)
			if attached area as l_area then
				from i := 0 until i = l_area.count or attached Result loop
					if attached l_area [i] as field and then not field.is_expanded
						and then field.type_id = type_id and then field.value (object) = value
					then
						Result := field
					end
					i := i + 1
				end
			end
		end

	field_with_address (object: EL_REFLECTIVE; field_address: POINTER): detachable EL_REFLECTED_FIELD
		-- Reflected field in `object' with field address equal to `field_address'.
		-- `Void' if not found.
		local
			i: INTEGER
		do
			if attached area as l_area then
				from i := 0 until i = l_area.count or attached Result loop
					if attached l_area [i] as field and then field.address (object) = field_address then
						Result := field
					end
					i := i + 1
				end
			end
		end

	indices_set: EL_FIELD_INDICES_SET
		local
			i: INTEGER
		do
			create Result.make_empty_area (count)
			if attached area as field then
				from i := 0 until i = field.count loop
					Result.extend (field [i].index)
					i := i + 1
				end
			end
		end

	name_list: EL_ARRAYED_LIST [IMMUTABLE_STRING_8]
		do
			create Result.make_filled (count, agent i_th_name)
		end

	query_by_type (type: TYPE [EL_REFLECTED_FIELD]): EL_ARRAYED_LIST [EL_REFLECTED_FIELD]
		-- list of reflected fields of `type'
		local
			type_id, i: INTEGER
		do
			if attached {like query_by_type} Arrayed_list_factory.new_list (type, count) as new
				and then attached area as l_area
			then
				Result := new
				type_id := type.type_id
				from i := 0 until i = l_area.count loop
					if attached l_area [i] as field and then {ISE_RUNTIME}.dynamic_type (field) = type_id then
						Result.extend (field)
					end
					i := i + 1
				end
			else
				create Result.make_empty
			end
		end

	special_subset (excluded_set: EL_FIELD_INDICES_SET): SPECIAL [EL_REFLECTED_FIELD]
		local
			field_list: ARRAYED_LIST [like item]; i: INTEGER
		do
			if excluded_set.count = 0 then
				Result := area

			elseif attached area as l_area then
				create field_list.make (count - excluded_set.count)
				from i := 0 until i = l_area.count loop
					if attached l_area [i] as field and then not excluded_set.has (field.index) then
						field_list.extend (field)
					end
					i := i + 1
				end
				field_list.trim
				Result := field_list.area
			else
				create Result.make_empty (0)
			end
		end

	table: EL_FIELD_TABLE
		-- field table looked up by `item.name'

	type_set: like type_table.item_list
		-- set of types use in table
		do
			Result := type_table.item_list
		end

	type_table: EL_HASH_TABLE [TYPE [ANY], INTEGER]
		local
			i: INTEGER
		do
			create Result.make_equal (count)
			if attached area as l_area then
				from i := 0 until i = l_area.count loop
					if attached l_area [i] as field then
						Result.put (field.type, field.type_id)
					end
					i := i + 1
				end
			end
		end

	value_list_for_type (object: EL_REFLECTIVE; field_type: TYPE [ANY]): EL_ARRAYED_LIST [ANY]
		-- list of field values in `object' for fields with type `field_type'
		local
			type_id, i: INTEGER
		do
			if attached Arrayed_list_factory.new_list (field_type, count) as new
				and then attached area as l_area
			then
				Result := new
				type_id := field_type.type_id
				from i := 0 until i = l_area.count loop
					if attached l_area [i] as field and then field.type_id = type_id then
						if field.is_expanded then
							Result.extend (field.value (object))

						elseif attached field.value (object) as value then
							Result.extend (value)
						end
					end
					i := i + 1
				end
			else
				create Result.make_empty
			end
		end

feature -- Status query

	has_default_strings (object: EL_REFLECTIVE): BOOLEAN
		-- `True' if all string fields in `object' are empty
		local
			i: INTEGER
		do
			Result := True
			if attached area as l_area then
				from i := 0 until i = l_area.count or not Result loop
					if attached l_area [i] as field and then field.is_string_type
						and then attached {EL_REFLECTED_STRING [READABLE_STRING_GENERAL]} field as string_field
					then
						Result := string_field.value (object).is_empty
					end
					i := i + 1
				end
			end
		end

feature -- Comparison

	all_equal (a_current, other: EL_REFLECTIVE): BOOLEAN
		-- `True' if all fields in `a_current' and `other' are equal
		local
			i: INTEGER
		do
			Result := True
			if attached area as field then
				from i := 0 until i = field.count or not Result loop
					Result := field [i].are_equal (a_current, other)
					i := i + 1
				end
			end
		end

	same_fields (a_current, other: EL_REFLECTIVE; field_set: EL_FIELD_INDICES_SET): BOOLEAN
		-- `True' if all fields with `field.index' in `field_set' have same value
		-- for `a_current' and `other' enclosing objects
		local
			i: INTEGER
		do
			Result := True
			if attached area as l_area then
				from i := 0 until i = l_area.count or not Result loop
					if attached l_area [i] as field and then field_set.has (field.index) then
						Result := field.are_equal (a_current, other)
					end
					i := i + 1
				end
			end
		end

feature -- Contract Support

	is_valid: BOOLEAN
		-- `True' if `table' has same count as `Current' and items are in same order
		do
			if table.count = count then
				Result := across table as field all
					field.key = i_th (field.cursor_index).name
				end
			end
		end

feature -- Basic operations

	set_all_from_readable (target: EL_REFLECTIVE; readable: EL_READABLE)
		local
			i: INTEGER
		do
			if attached area as field then
				from i := 0 until i = field.count loop
					field [i].set_from_readable (target, readable)
					i := i + 1
				end
			end
		end

	sink (target: EL_REFLECTIVE; sinkable: EL_DATA_SINKABLE)
		local
			i: INTEGER
		do
			if attached area as field then
				from i := 0 until i = field.count loop
					field [i].write (target, sinkable)
					i := i + 1
				end
			end
		end

	write (target: EL_REFLECTIVE; a_writable: EL_WRITABLE)
		local
			i: INTEGER_32
		do
			if attached area as field then
				from i := 0 until i = field.count loop
					field [i].write (target, a_writable)
					i := i + 1
				end
			end
		end

	write_to_memory (target: EL_REFLECTIVE; memory: EL_MEMORY_READER_WRITER)
		local
			i: INTEGER_32
		do
			if attached area as field then
				from i := 0 until i = field.count loop
					field [i].write_to_memory (target, memory)
					i := i + 1
				end
			end
		end

feature {NONE} -- Implementation

	fill_table (representation_table: EL_HASH_TABLE [EL_FIELD_REPRESENTATION [ANY, ANY], STRING])
		-- fill `table' and set field representations to make `Current.is_valid' equal to `True'
		local
			i: INTEGER
		do
			from i := 1 until i > count loop
				if attached i_th (i) as field then
					table.extend (field, field.name)
					if representation_table.has_key (field.name) then
						field.set_representation (representation_table.found_item)
					end
				end
				i := i + 1
			end
		end

	i_th_name (i: INTEGER): IMMUTABLE_STRING_8
		do
			Result := i_th (i).name
		end

	set_order (order: EL_FIELD_LIST_ORDER; field_info_table: EL_OBJECT_FIELDS_TABLE)
		require
			table_not_filled: table.count = 0
		local
			i, offset, i_final: INTEGER; l_indices_set: EL_FIELD_INDICES_SET
		do
		-- apply `name_sort' sort if attached
			if attached order.name_sort as name_sort then
				order_by (name_sort, True)
			end
		-- apply field order shifts if not default
			if order.field_shifts.count > 0 then
				across order.field_shifts as list loop
					i := list.item.index; offset := list.item.offset
					if valid_shift (i, offset) then
						shift_i_th (i, offset)
					end
				end
			end
		-- move any explicitly ordered fields to the end of list
			if order.reordered_fields.count > 0 then
				create l_indices_set.make (field_info_table, order.reordered_fields)
				i_final := l_indices_set.count
				from i := 0 until i = i_final loop
					find_first_equal (l_indices_set [i], agent {EL_REFLECTED_FIELD}.index)
					if found then
						shift (count - index)
					end
					i := i + 1
				end
			end
		end

feature {NONE} -- Internal attributes

	foreign_naming: detachable EL_NAME_TRANSLATER

end