class EL_STRING_X_ROUTINES
note
description: "Routines to supplement handling of ${STRING_8} ${STRING_32} strings"
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-08-24 10:35:08 GMT (Saturday 24th August 2024)"
revision: "76"
deferred class
EL_STRING_X_ROUTINES [
STRING_X -> STRING_GENERAL create make end, READABLE_STRING_X -> READABLE_STRING_GENERAL,
C -> COMPARABLE -- CHARACTER_X
]
inherit
EL_READABLE_STRING_X_ROUTINES [READABLE_STRING_X, C]
feature -- Basic operations
replace (str: STRING_X; content: READABLE_STRING_GENERAL)
-- replace all characters of `str' wih new `content'
do
wipe_out (str)
append_to (str, content)
end
feature -- Factory
character_string (uc: CHARACTER_32): STRING_X
-- shared instance of string with `uc' character
deferred
end
n_character_string (uc: CHARACTER_32; n: INTEGER): STRING_X
-- shared instance of string with `n' times `uc' character
deferred
ensure
valid_result: Result.occurrences (uc) = n.to_integer_32
end
new (n: INTEGER): STRING_X
-- width * count spaces
do
create Result.make (n)
end
new_from_string_8 (str: READABLE_STRING_8; utf_8_encoded: BOOLEAN): STRING_X
local
u8: EL_UTF_8_CONVERTER
do
if utf_8_encoded then
create Result.make (u8.unicode_count (str))
append_utf_8_to (str, Result)
else
Result := new (str.count)
Result.append (str)
end
end
new_from_immutable_8 (
str: IMMUTABLE_STRING_8; start_index, end_index: INTEGER_32; unindent, utf_8_encoded: BOOLEAN
): STRING_X
-- new string of type `STRING_X' consisting of decoded UTF-8 text
-- `str.shared_substring (start_index, end_index)'
-- if `unindent' is true, and character at `start_index' is a tab, then all lines
-- are unindented by one tab.
require
valid_start_index: str.valid_index (start_index)
valid_end_index: end_index >= start_index implies str.valid_index (end_index)
local
split_list: EL_SPLIT_IMMUTABLE_STRING_8_LIST
do
if end_index - start_index + 1 = 0 then
create Result.make (0)
elseif unindent and then str [start_index] = '%T' then
if attached str.shared_substring (start_index + 1, end_index) as lines then
if lines.has ('%N') then
if utf_8_encoded then
split_list := Immutable_utf_8_list
else
split_list := Immutable_latin_1_list
end
split_list.fill_by_string (lines, New_line_tab, 0)
if split_list.count > 0 then
create Result.make (split_list.unicode_count + split_list.count - 1)
split_list.append_lines_to (Result)
end
else
Result := new_from_string_8 (lines, utf_8_encoded)
end
end
else
Result := new_from_string_8 (str.shared_substring (start_index, end_index), utf_8_encoded)
end
ensure
calculated_correct_size: Result.count = Result.capacity
end
new_list (comma_separated: STRING_X): EL_STRING_LIST [STRING_X]
deferred
end
new_retrieved (file_path: FILE_PATH): STRING_X
-- new instace of type `STRING_X' restored from file saved by Studio debugger
local
file: RAW_FILE
do
create file.make_open_read (file_path)
if attached {STRING_X} file.retrieved as debug_str then
Result := debug_str
else
Result := new (0)
end
file.close
end
shared_substring (s: STRING_X; new_count: INTEGER): STRING_X
-- `s.substring (1, new_count)' with shared area
require
valid_count: new_count <= s.count
deferred
end
feature -- List joining
joined (a, b: READABLE_STRING_X): STRING_X
do
create Result.make (a.count + b.count)
append_to (Result, a); append_to (Result, b)
end
joined_lines (list: ITERABLE [READABLE_STRING_GENERAL]): STRING_X
do
Result := joined_list (list, '%N')
end
joined_list (list: ITERABLE [READABLE_STRING_GENERAL]; separator: CHARACTER_32): STRING_X
local
char_count: INTEGER; code: NATURAL_32
do
code := to_code (separator) -- might be z_code for ZSTRING
char_count := character_count (list, 1)
create Result.make (char_count)
across list as ln loop
if Result.count > 0 then
Result.append_code (code)
end
append_to (Result, ln.item)
end
end
joined_list_with (list: ITERABLE [READABLE_STRING_GENERAL]; separator: READABLE_STRING_GENERAL): STRING_X
local
char_count: INTEGER
do
char_count := character_count (list, separator.count)
create Result.make (char_count)
across list as ln loop
if Result.count > 0 then
append_to (Result, separator)
end
append_to (Result, ln.item)
end
end
joined_with (a, b, separator: READABLE_STRING_X): STRING_X
do
create Result.make (a.count + b.count + separator.count)
append_to (Result, a); append_to (Result, separator); append_to (Result, b)
end
feature -- Transformed
bracketed (str: READABLE_STRING_X; left_bracket: CHARACTER_32): STRING_X
-- substring of `str' enclosed by one of matching paired characters: {}, [], (), <>
-- Empty string if `not str.has (left_bracket)' or no matching right bracket
require
valid_left_bracket: (create {EL_CHARACTER_32_ROUTINES}).is_left_bracket (left_bracket)
local
left_index, right_index: INTEGER; content: READABLE_STRING_GENERAL
c32: EL_CHARACTER_32_ROUTINES
do
left_index := str.index_of (left_bracket, 1)
if left_index > 0 and then attached cursor (str) as l_cursor then
right_index := str.index_of (c32.right_bracket (left_bracket), left_index + 1)
right_index := l_cursor.matching_bracket_index (left_index)
if right_index > 0 then
content := str.substring (left_index + 1, right_index - 1)
create Result.make (content.count)
append_to (Result, content)
else
create Result.make (0)
end
else
create Result.make (0)
end
end
curtailed (str: READABLE_STRING_X; max_count: INTEGER): READABLE_STRING_X
-- `str' curtailed to `max_count' with added ellipsis where `max_count' is exceeded
do
if str.count > max_count - 2 then
Result := str.substring (1, max_count - 2) + Character_string_8_table.item ('.', 2)
else
Result := str
end
end
enclosed (str: READABLE_STRING_GENERAL; left, right: CHARACTER_32): STRING_X
--
do
create Result.make (str.count + 2)
Result.append_code (to_code (left))
append_to (Result, str)
Result.append_code (to_code (right))
end
leading_delimited (text, delimiter: STRING_X; include_delimiter: BOOLEAN): STRING_X
--
do
if attached Shared_intervals as intervals then
intervals.wipe_out
intervals.fill_by_string_general (text, delimiter, 0)
intervals.start
if intervals.after then
create Result.make (0)
else
if include_delimiter then
Result := text.substring (1, intervals.item_upper)
else
Result := text.substring (1, intervals.item_lower - 1)
end
end
else
create Result.make (0)
end
end
pruned (str: READABLE_STRING_GENERAL; c: CHARACTER_32): STRING_X
deferred
end
quoted (str: READABLE_STRING_GENERAL; quote_type: INTEGER): STRING_X
require
single_or_double: (1 |..| 2).has (quote_type)
local
c: CHARACTER
do
inspect quote_type
when 1 then
c := '%''
else
c := '"'
end
Result := enclosed (str, c, c)
end
replaced_identifier (str, old_id, new_id: READABLE_STRING_X): STRING_X
-- copy of `str' with each each Eiffel identifier `old_id' replaced with `new_id'
require
both_identifiers: is_eiffel (old_id) and is_eiffel (new_id)
local
intervals: EL_OCCURRENCE_INTERVALS
do
create intervals.make_by_string (str, old_id)
if new_id.count > old_id.count then
Result := new (str.count + (new_id.count - old_id.count) * intervals.count)
else
Result := new (str.count)
end
Result.append (str)
from intervals.finish until intervals.before loop
if is_identifier_boundary (str, intervals.item_lower, intervals.item_upper) then
replace_substring (Result, new_id, intervals.item_lower, intervals.item_upper)
end
intervals.back
end
end
feature -- Adjust
prune_all_leading (str: STRING_X; c: CHARACTER_32)
deferred
end
remove_bookends (str: STRING_X; ends: READABLE_STRING_GENERAL)
require
ends_has_2_characters: ends.count = 2
do
if str.item (1) = ends.item (1) then
str.keep_tail (str.count - 1)
end
if str.item (str.count) = ends.item (2) then
str.set_count (str.count - 1)
end
end
remove_double_quote (quoted_str: STRING_X)
--
do
remove_bookends (quoted_str, once "%"%"" )
end
remove_single_quote (quoted_str: STRING_X)
--
do
remove_bookends (quoted_str, once "''" )
end
to_canonically_spaced (str: STRING_X)
-- adjust string so that `is_canonically_spaced' becomes true
local
uc_i: CHARACTER_32; i, j, upper, space_count: INTEGER
c32: EL_CHARACTER_32_ROUTINES
do
if attached {READABLE_STRING_X} str as s and then not is_canonically_spaced (s) then
upper := str.count
from i := 1; j := 1 until i > upper loop
uc_i := str [i]
if c32.is_space (uc_i) then
space_count := space_count + 1
else
space_count := 0
end
inspect space_count
when 0 then
str.put_code (uc_i.natural_32_code, j)
j := j + 1
when 1 then
str.put_code ({EL_ASCII}.space, j)
j := j + 1
else
end
i := i + 1
end
str.keep_head (j - 1)
end
end
wipe_out (str: STRING_X)
deferred
end
feature -- Transform
first_to_upper (str: STRING_GENERAL)
do
if not str.is_empty then
str.put_code (to_code (str.item (1).as_upper), 1)
end
end
replace_character (target: STRING_X; uc_old, uc_new: CHARACTER_32)
local
i: INTEGER; code_old, code_new: NATURAL
do
code_old := to_code (uc_old); code_new := to_code (uc_new)
from i := 1 until i > target.count loop
if target.code (i) = code_old then
target.put_code (code_new, i)
end
i := i + 1
end
end
translate (target: STRING_X; old_characters, new_characters: READABLE_STRING_GENERAL)
-- replace all characters in `old_characters' with corresponding character in `new_characters'.
do
translate_with_deletion (target, old_characters, new_characters, False)
end
translate_or_delete (target: STRING_X; old_characters, new_characters: READABLE_STRING_GENERAL)
-- replace all characters in `old_characters' with corresponding character in `new_characters'.
-- and removing any characters corresponding to null value '%U'
do
translate_with_deletion (target, old_characters, new_characters, True)
end
feature {NONE} -- Implementation
translate_with_deletion (
target: STRING_X; old_characters, new_characters: READABLE_STRING_GENERAL; delete_null: BOOLEAN
)
require
each_old_has_new: old_characters.count = new_characters.count
local
source: STRING_X; uc: CHARACTER_32; i, index: INTEGER
new_code: NATURAL
do
source := target.twin
target.set_count (0)
from i := 1 until i > source.count loop
uc := source [i]
index := old_characters.index_of (uc, 1)
if index > 0 then
new_code := to_code (new_characters [index])
if delete_null implies new_code > 0 then
target.append_code (new_code)
end
else
target.append_code (to_code (uc))
end
i := i + 1
end
end
feature {NONE} -- Deferred
append_area_32 (str: STRING_X; area: SPECIAL [CHARACTER_32])
deferred
end
append_utf_8_to (utf_8: READABLE_STRING_8; output: STRING_X)
deferred
end
append_to (str: STRING_X; extra: READABLE_STRING_GENERAL)
deferred
end
replace_substring (str: STRING_X; insert: READABLE_STRING_X; start_index, end_index: INTEGER)
deferred
end
set_upper (str: STRING_X; i: INTEGER)
require
valid_index: 0 < i and i <= str.count
deferred
end
feature {NONE} -- Constants
New_line_tab: STRING = "%N%T"
Immutable_utf_8_list: EL_SPLIT_IMMUTABLE_UTF_8_LIST
once
create Result.make_empty
end
Immutable_latin_1_list: EL_SPLIT_IMMUTABLE_STRING_8_LIST
once
create Result.make_empty
end
end