Introducing Class EL_STRING_EDITOR
I have added a new class EL_STRING_EDITOR to Eiffel-Loop that is very useful when you want to edit the contents of a string between all occurrences of a pair of delimiters.
A typical application is editing the text between markup tags. The beauty of this class is an agent routine allows you edit all delimited sections as an isolated string. The routine for_each
takes care of incorporating the edits into the target string.
It has been added to github but is not yet part of any release.
Descendants
EL_STRING_EDITOR*
EL_ZSTRING_EDITOR
EL_STRING_8_EDITOR
EL_STRING_32_EDITOR
For convenience a new routine edit
has been add to class EL_ZSTRING that applies the editor to Current
.
edit (left_delimiter, right_delimiter: READABLE_STRING_GENERAL; a_edit: PROCEDURE [INTEGER, INTEGER, ZSTRING])
local
editor: EL_ZSTRING_EDITOR
do
create editor.make (Current)
editor.for_each (left_delimiter, right_delimiter, a_edit)
An Example
Here is a "before and after" example that demonstrates expansion of Wiki-markup with and without using class EL_ZSTRING_EDITOR. See routine substitute_html
.
Without using EL_STRING_EDITOR
Getting the indices right is quite tricky as editing the text changes the offsets.
class
MARKUP_SUBSTITUTION
inherit
EL_STRING_CONSTANTS
create
make, make_hyperlink
feature {NONE} -- Initialization
make (a_delimiter_start, a_delimiter_end, a_markup_open, a_markup_close: ZSTRING)
do
delimiter_start := a_delimiter_start; delimiter_end := a_delimiter_end
markup_open := a_markup_open; markup_close := a_markup_close
end
make_hyperlink (a_delimiter_start: ZSTRING)
do
make (a_delimiter_start, Right_square_bracket, Empty_string, Empty_string)
is_hyperlink := True
end
feature -- Status query
is_hyperlink: BOOLEAN
feature -- Basic operations
substitute_html (html: ZSTRING; new_expanded_link: FUNCTION [ZSTRING, ZSTRING, ZSTRING])
local
pos_open, pos_close, pos_space: INTEGER; done: BOOLEAN
expanded_link, link_path, link_text: ZSTRING
do
from until done loop
pos_open := html.substring_index (delimiter_start, pos_open + 1)
if pos_open > 0 then
pos_close := html.substring_index (delimiter_end, pos_open + delimiter_start.count)
if pos_close > 0 then
if is_hyperlink then
pos_space := index_of_white_space (html, pos_open + delimiter_start.count, pos_close - 1)
if pos_space > 0 and then pos_space < pos_close then
link_path := html.substring (pos_open + 1, pos_space - 1)
link_text := html.substring (pos_space + 1, pos_close - 1)
else
link_path := html.substring (pos_open + 1, pos_close - 1)
link_text := link_path
end
expanded_link := new_expanded_link (link_path, link_text)
html.replace_substring (expanded_link, pos_open, pos_close)
pos_open := html.index_of ('>', pos_open)
else
html.replace_substring (markup_open, pos_open, pos_open + delimiter_start.count - 1)
pos_close := pos_close + markup_open.count - delimiter_start.count
html.replace_substring (markup_close, pos_close, pos_close + delimiter_end.count - 1)
end
end
end
done := pos_open = 0 or pos_close = 0
end
end
feature -- Access
delimiter_end: ZSTRING
delimiter_start: ZSTRING
markup_close: ZSTRING
markup_open: ZSTRING
feature {NONE} -- Implementation
index_of_white_space (html: ZSTRING; start_index, end_index: INTEGER): INTEGER
-- index of white space character just before text
-- (allowing links to be spread across 2 lines)
local
i: INTEGER
do
from i := start_index until Result > 0 or i > end_index loop
if html.is_space_item (i) then
Result := i
end
i := i + 1
end
if Result > 0 then
from Result := 0 until Result > 0 or i > end_index loop
if not html.is_space_item (i) then
Result := i - 1
end
i := i + 1
end
end
end
feature {NONE} -- Constants
Right_square_bracket: ZSTRING
once
Result := "]"
end
end
Using EL_STRING_EDITOR
Note there is no longer any use for routine index_of_white_space
which managed the case of a hyperlink being split across lines. Routine to_canonically_spaced
gets rid of any tabs and new lines.
The code is much simpler because you now only have to think of indices that are relative to the the delimited substring rather then the full target string. The start_index
and end_index
of the delimited section has already been calculated for you.
class
MARKUP_SUBSTITUTION
inherit
EL_STRING_CONSTANTS
create
make, make_hyperlink
feature {NONE} -- Initialization
make (a_delimiter_start, a_delimiter_end, a_markup_open, a_markup_close: ZSTRING)
do
delimiter_start := a_delimiter_start; delimiter_end := a_delimiter_end
markup_open := a_markup_open; markup_close := a_markup_close
new_expanded_link := agent empty_link
end
make_hyperlink (a_delimiter_start: ZSTRING)
do
make (a_delimiter_start, Right_square_bracket, Empty_string, Empty_string)
is_hyperlink := True
end
feature -- Status query
is_hyperlink: BOOLEAN
feature -- Basic operations
substitute_html (html: ZSTRING; new_link_agent: like new_expanded_link)
do
if is_hyperlink then
new_expanded_link := new_link_agent
html.edit (delimiter_start, delimiter_end, agent expand_hyperlink_markup)
else
html.edit (delimiter_start, delimiter_end, agent expand_markup)
end
end
feature -- Access
delimiter_end: ZSTRING
delimiter_start: ZSTRING
markup_close: ZSTRING
markup_open: ZSTRING
feature {NONE} -- Implementation
expand_markup (start_index, end_index: INTEGER; substring: ZSTRING)
do
substring.replace_substring (markup_close, end_index + 1, substring.count)
substring.replace_substring (markup_open, 1, start_index - 1)
end
expand_hyperlink_markup (start_index, end_index: INTEGER; substring: ZSTRING)
local
expanded_link, link_path, link_text: ZSTRING
space_index: INTEGER
do
substring.to_canonically_spaced
space_index := substring.index_of (' ', 1)
if space_index > 0 then
link_path := substring.substring (2, space_index - 1)
link_text := substring.substring (space_index + 1, substring.count - 1)
else
link_path := substring.substring (2, substring.count - 1)
link_text := link_path
end
substring.share (new_expanded_link (link_path, link_text))
end
empty_link (path, text: ZSTRING): ZSTRING
do
create Result.make_empty
end
new_expanded_link: FUNCTION [ZSTRING, ZSTRING, ZSTRING]
feature {NONE} -- Constants
Right_square_bracket: ZSTRING
once
Result := "]"
end
end