class EL_ITERABLE_SPLIT_CURSOR

(source code)

description

Cursor for target string sections split by separator of type G

note
	description: "Cursor for `target' string sections split by separator of type **G**"

	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: "2023-11-16 15:40:46 GMT (Thursday 16th November 2023)"
	revision: "12"

deferred class
	EL_ITERABLE_SPLIT_CURSOR [S -> READABLE_STRING_GENERAL, G]

inherit
	ITERATION_CURSOR [S]
		rename
			item as item_copy
		end

	EL_LIST_ITEM_SUBSTRING_CONVERSION

feature {NONE} -- Initialization

	initialize
		do
		end

	make (a_target: like target; a_separator: like separator; left_adjust, right_adjust: BOOLEAN)
		do
			target := a_target; separator := a_separator
			left_adjusted := left_adjust; right_adjusted := right_adjust
			initialize
			forth
		end

feature -- Access

	cursor_index: INTEGER

	item: like target
		-- dynamic singular substring of `target' at current split position if the
		-- `target' conforms to `STRING_GENERAL' else the value of `item_copy'
		-- use `item_copy' if you intend to keep a reference to `item' beyond the scope of the
		-- client routine
		do
			if attached {STRING_GENERAL} target as l_target then
				if attached {STRING_GENERAL} internal_item as l_item then
					l_item.keep_head (0)
					l_item.append_substring (l_target, item_lower, item_upper)
					Result := internal_item
				else
					Result := item_copy
					internal_item := Result
				end
			else
				Result := item_copy
			end
		end

	item_copy: like target
		-- new substring of `target' at current split position
		do
			Result := target.substring (item_lower, item_upper)
		end

	item_count: INTEGER
		do
			Result := item_upper - item_lower + 1
		end

	item_index_of (uc: CHARACTER_32; start_index: INTEGER): INTEGER
		local
			i: INTEGER
		do
			if start_index <= item_count then
				from i := item_lower + start_index - 1 until i > item_upper or else Result > 0 loop
					if target [i] = uc then
						Result := i - item_lower + 1
					end
					i := i + 1
				end
			end
		end

	item_upper: INTEGER
		-- end index of `item' in `target' string

	item_lower: INTEGER
		-- start index of `item' in `target' string

feature -- Optimized operations

	append_item_to (general: STRING_GENERAL)
		-- equivalent to appending `item' to `general'
		do
			general.append_substring (target, item_lower, item_upper)
		end

	fill_with_item (general: STRING_GENERAL)
		-- equivalent to `general.replace_substring (1, general.count, item)' even
		-- though routine `replace_substring' on exists in descendants
		do
			general.keep_head (0)
			general.append_substring (target, item_lower, item_upper)
		end

	sink_item_to (sink: EL_DATA_SINKABLE)
		do
		end

feature -- Status query

	after: BOOLEAN
		-- Are there no more items to iterate over?

	is_last: BOOLEAN
		-- `True' if cursor is currently on the last `item'

	item_has (uc: CHARACTER_32): BOOLEAN
		local
			i: INTEGER
		do
			from i := item_lower until i > item_upper or else Result loop
				Result := target [i] = uc
				i := i + 1
			end
		end

	item_is_empty: BOOLEAN
		do
			Result := item_upper < item_lower
		end

	item_same_as (str: like target): BOOLEAN
		do
			if item_upper - item_lower + 1 = str.count then
				if str.count = 0 then
					Result := item_upper + 1 = item_lower
				else
					Result := same_characters (target, str, 1, str.count, item_lower)
				end
			end
		end

	item_same_caseless_as (str: like target): BOOLEAN
		do
			if item_upper - item_lower + 1 = str.count then
				if str.count = 0 then
					Result := item_upper + 1 = item_lower
				else
					Result := same_caseless_characters (target, str, 1, str.count, item_lower)
				end
			end
		end

	item_starts_with (str: like target): BOOLEAN
		do
			if str.count = 0 then
				Result := True
			elseif item_upper - item_lower + 1 >= str.count then
				Result := same_characters (target, str, 1, str.count, item_lower)
			end
		end

	left_adjusted: BOOLEAN

	right_adjusted: BOOLEAN

feature -- Cursor movement

	forth
		-- Move cursor to next position.
		local
			previous_separator_end: INTEGER; found_first: BOOLEAN
		do
			if is_last then
				after := True
			else
				previous_separator_end := separator_end
				set_separator_start
				if separator_start > 0 then
					separator_end := separator_start + separator_count - 1
					item_lower := previous_separator_end + 1
					item_upper := separator_start - 1
				else
					item_lower := separator_end + 1
					item_upper := target.count
					is_last := True
				end
				if left_adjusted and then attached target as l_target then
					from until found_first or else item_lower > item_upper loop
						if is_white_space (l_target, item_lower) then
							item_lower := item_lower + 1
						else
							found_first := True
						end
					end
				end
				if right_adjusted and then attached target as l_target then
					found_first := False
					from until found_first or else item_upper < item_lower  loop
						if is_white_space (l_target, item_upper) then
							item_upper := item_upper - 1
						else
							found_first := True
						end
					end
				end
				cursor_index := cursor_index + 1
			end
		end

feature {NONE} -- Implementation

	is_white_space (a_target: like target; i: INTEGER): BOOLEAN
		local
			c32: EL_CHARACTER_32_ROUTINES
		do
			Result := c32.is_space (a_target [i])
		end

 	same_caseless_characters (a_target, other: like target; start_pos, end_pos, index_pos: INTEGER): BOOLEAN
			-- Are characters of `other' within bounds `start_pos' and `end_pos'
			-- caseless identical to characters of current string starting at index `index_pos'.
		do
			Result := a_target.same_caseless_characters (other, start_pos, end_pos, index_pos)
		end

 	same_characters (a_target, other: like target; start_pos, end_pos, index_pos: INTEGER): BOOLEAN
			-- Are characters of `other' within bounds `start_pos' and `end_pos'
			-- identical to characters of current string starting at index `index_pos'.
		do
			Result := a_target.same_characters (other, start_pos, end_pos, index_pos)
		end

	set_separator_start
		deferred
		end

feature {NONE} -- Internal attributes

	internal_item: detachable like target

	separator: G

	separator_count: INTEGER
		deferred
		end

	separator_end: INTEGER
		-- end index of separator

	separator_start: INTEGER
		-- start index of separator

	target: S

end