ST_STRING_BUILDER

Colin Paul Adams <[email protected]> Sat, 15 Nov 2008 22:06:26 +0000
Newsgroups gmane.comp.lang.eiffel.gobo.devel
Message-ID <[email protected]>
indexing

	description:

		"[
        Objects for constructing XPath UTF-32 strings.
        Usage pattern is:
        1) Create builder.
        2) Call appending features.
        3) Call `set_built' followed by `new_string'.
        4) Call `reuse' and loop to (2).
       ]"

	library: "Gobo Eiffel String Library"
	copyright: "Copyright (c) 2008, Colin Adams and others"
	license: "MIT License"
	date: "$Date:  $"
	revision: "$Revision:  $"

class ST_STRING_BUILDER

inherit
	
	ANY

	ST_STRING_ROUTINES
		export {NONE} all end

	ST_SHARED_STRINGS
		export {NONE} all end

create

	make

feature {NONE} -- Initialization

	make (a_capacity: INTEGER) is
			-- Initialize for a string of expected size `a_capacity'.
		require
			a_capacity_non_negative: a_capacity >= 0
		do
			create array.make (1, a_capacity)
			count := 0
			is_building := True
		ensure
			correct_capacity: array.count = a_capacity
			empty: count = 0
			ready_to_build: is_building
		end

feature -- Initialization

	reuse (a_capacity: INTEGER) is
			-- Prepare to reuse `Current' for a string of expected size `a_capacity'.
		require
			a_capacity_non_negative: a_capacity >= 0
			not_ready: not is_building
		do
			array.conservative_resize (1, a_capacity)
			count := 0
			is_building := True
		ensure
			correct_capacity: array.capacity >= a_capacity
			empty: count = 0
			ready_to_build: is_building
		end

feature -- Access

	new_string: ST_STRING is
			-- Newly constructed string
		require
			built: not is_building
		do
			if count = 0 then
				Result := shared_empty_string
			else
				create Result.make_from_codepoints (array.subarray (1, count.as_integer_32))
			end
		ensure
			new_string_not_void: Result /= Void
		end
	
	index_of_code (a_code: NATURAL_32): INTEGER is
			-- Index of first built character matching `a_code';
			-- Result = 0 = no match
		require
			ready_to_build: is_building
		local
			l_finished: BOOLEAN
		do
			from
				Result := 1 
			until
				l_finished
			loop
				if Result > count then
					Result := 0
					l_finished := True
				elseif array.item (Result) = a_code then
					l_finished := True
				else
					Result := Result + 1
				end
			end
		ensure
			index_of_code_in_range: Result >= 0 and Result <= count
		end
	
	code (a_index: INTEGER): NATURAL_32 is
			-- code at `a_index'
		require
			ready_to_build: is_building
			good_index: a_index > 0 and a_index <= count
		do
			Result := array.item (a_index)
		end

feature -- Measurement

	count: INTEGER
			-- Number of characters added to `Current' since last call to `reuse'
	
	is_empty: BOOLEAN is
			-- Have any characters been added?
		do
			Result := count = 0
		ensure
			definition: Result = (count = 0)
		end
			
feature -- Status report

	is_building: BOOLEAN
			-- Is `Current' ready for receiving characters?

feature -- Status setting

	set_built is
			-- Set `is_building' to `False'.
		do
			is_building := False
		ensure
			not_ready: not is_building
		end

feature -- Basic operations

	append_character (a_character: CHARACTER_8) is
			-- Add `a_character' at end of `Current'.
		require
			ready_to_build: is_building
		do
			array.conservative_resize (1, 1 + count)
			array.put (a_character.code.as_natural_32, count + 1)
			count := count + 1
		ensure
			correct_count: count = old count + 1
			correct_character_appended: array.item (count) = a_character.code.as_natural_32
		end
	
	append_code (a_character: NATURAL_32) is
			-- Add `a_character' at end of `Current'.
		require
			ready_to_build: is_building
		do
			array.conservative_resize (1, 1 + count)
			array.put (a_character, count + 1)
			count := count + 1
		ensure
			correct_count: count = old count + 1
			correct_character_appended: array.item (count) = a_character
		end
	
	append_code_multiple (a_character: NATURAL_32; a_count: INTEGER) is
			-- Add `a_character' at end of `Current' `a_count' times.
		require
			ready_to_build: is_building
			strictly_positive_count: a_count > 0
		local
			i: INTEGER
		do
			array.conservative_resize (1, a_count + count)
			from
				i := 1
			until
				i > a_count
			loop
				array.put (a_character, count + 1)
				count := count + 1
				i := i + 1
			end
		ensure
			correct_count: count = old count + a_count
			correct_character_appended: array.subarray (old count + 1, count).for_all (agent matches_code (?, a_character))
		end

	replace_code (a_character: NATURAL_32; a_index: INTEGER) is
			-- Replace `a_character' at `a_index'
		require
			ready_to_build: is_building
			a_index_valid: a_index > 0 and a_index <= count
		do
			array.put (a_character, a_index)
		ensure
			count_unchanged: count = old count
			correct_character_replaced: array.item (a_index) = a_character
		end

	append_string (a_string: READABLE_STRING_GENERAL) is
			-- Add all characters from `a_string' at end of `Current'.
		require
			a_string_not_void: a_string /= Void
			ready_to_build: is_building
		local
			i, l_count, l_new_count: INTEGER
		do
			l_count := a_string.count
			if l_count > 0 then
				l_new_count := count + l_count
				array.conservative_resize (1, l_new_count)
				from
					i := 1
				until
					i > l_count
				loop
					array.put (a_string.code (i), count + i)
					i := i + 1
				end
				count := l_new_count
			end
		ensure
			correct_count: count = old count + a_string.count
			correct_characters_appended: not a_string.is_empty implies characters_match (a_string, old count + 1, count)
		end

	append_strings (a_strings: ARRAY [READABLE_STRING_GENERAL]) is
			-- Add all characters from all of `a_strings' at end of `Current'.
		require
			a_strings_not_void: a_strings /= Void
			all_strings_not_void: not a_strings.has (Void)
			ready_to_build: is_building
		local
			j, k: INTEGER
			i, l_count, l_new_count: INTEGER
			l_string: READABLE_STRING_GENERAL
		do
			l_count := string_sums (a_strings)
			l_new_count := count + l_count
			if l_new_count > 0 then
				array.conservative_resize (1, l_new_count)
				from
					j := 1
					i := 1
				until
					j > a_strings.count
				loop
					from
						l_string := a_strings.item (j)
						k := 1
					until
						k > l_string.count
					loop
						array.put (l_string.code (k), count + i)
						i := i + 1
						k := k + 1
					end
					j := j + 1
				end
				check
					correct_i: i = l_new_count + 1 - count
					-- `i' is incremented on every inner loop
				end
			end
			count := l_new_count
		ensure
			correct_count: count = old count + string_sums (a_strings)
			correct_characters_appended: True -- TODO
		end
	
feature -- Removal

	remove_tail (a_n: INTEGER) is
			-- Remove final `a_n' characters from `Current'.
			-- If `a_n' > `count' remove all characters.
		require
			a_n_non_negative: a_n >= 0
		do
			if a_n > count then
				count := 0
			else
				count := count - a_n
			end
		ensure
			zero_characters_if_a_n_larger_than_count: a_n > old count implies count = 0
			correct_count: a_n <= old count implies count = old count - a_n
		end

feature {NONE} -- Implementation	

	array: ARRAY [NATURAL_32]
			-- Character buffer

	characters_match (a_string: READABLE_STRING_GENERAL; a_start, a_end: INTEGER): BOOLEAN is
			-- Does character sequence of `a_string' match `array.subarray (a_start, a_end)'?
		require
			a_string_not_void: a_string /= Void
			a_start_strictly_positive: a_start > 0
			a_end_greater_than_a_start: a_end > a_start
			a_end_small_enough: a_end <= count
			correct_string_count: a_string.count = (a_end - a_start + 1)
		local
			i, j: INTEGER
		do
			from
				Result := True
				i := a_start
				j := 1
			until
				not Result or i > a_end
			loop
				Result := a_string.code (j) = array.item (i)
				i := i + 1
				j := j + 1
			end
		end
	
	matches_code (a_code, a_other: NATURAL_32): BOOLEAN is
			-- Does `a_code' equal `a_other'?
		do
			Result := a_code = a_other
		ensure
			definition: Result = (a_code = a_other)
		end

invariant

	array_not_void: array /= Void
	count_non_negative: count >= 0

end

-- 
Colin Adams
Preston Lancashire

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/