note
	description: "[
		Objects that redirect file output into a buffer. If the output exceeds the size of the buffer,
		only the first and the last part of the output are kept in the buffer.
	]"
	date: "$Date: 2013-05-20 23:15:17 +0000 (Mon, 20 May 2013) $"
	revision: "$Revision: 92557 $"

class 
	EQA_TEST_OUTPUT_BUFFER

inherit
	PLAIN_TEXT_FILE
		rename
			make as make_file,
			wipe_out as wipe_out_file
		redefine
			count,
			Exists,
			readable,
			put_boolean,
			put_real,
			put_double,
			put_string,
			put_character,
			put_new_line,
			new_line,
			end_of_file,
			file_close,
			putbool,
			putreal,
			putdouble,
			putstring,
			putchar,
			dispose,
			back,
			flush
		end

create 
	make

feature {NONE} -- Initialization

	make (a_size: like buffer_size)
			-- Initialize Current.
			--
			-- a_size: Buffer size limiting amount of text which will be buffered
		require
			a_size_greater_one: a_size > 3
		do
			buffer_size := a_size
			create buffer.make (buffer_size)
			mode := Write_file
			set_name ("test_output_buffer")
			create last_string.make_empty
		end
	
feature -- Access

	buffer_size: INTEGER_32
			-- Buffer size limiting amount of text which will be buffered

	count: INTEGER_32
			-- Size in bytes (0 if no associated physical file)
		do
			Result := buffer.count
		end

	content: like buffer
			-- Not truncated content printed to buffer
		require
			not_truncated: not is_truncated
		do
			create Result.make_from_string (buffer)
		end

	leading_content: like buffer
			-- First part of buffer (before truncated section)
		require
			truncated: is_truncated
		local
			l_pos: like split_position
		do
			l_pos := split_position
			create Result.make (l_pos)
			Result.append (buffer.substring (1, l_pos))
		end

	closing_content: like buffer
			-- Second part of buffer (after truncated section)
		require
			truncated: is_truncated
		local
			l_pos: like split_position
		do
			l_pos := split_position
			create Result.make (buffer_size - l_pos)
			Result.append (buffer.substring (truncated_start_position, buffer.count))
			if truncated_start_position > l_pos + 1 then
				Result.append (buffer.substring (l_pos + 1, truncated_start_position - 1))
			end
		end

	formatted_content: like buffer
			-- Buffered content formatted in a printable way
		do
			if is_truncated then
				create Result.make (buffer_size + 100)
				Result.append (leading_content)
				Result.append (M_truncated)
				Result.append (closing_content)
			else
				Result := content
			end
		end
	
feature {NONE} -- Access

	buffer: STRING_8
			-- Buffer containing first part of output

	split_position: INTEGER_32
			-- Position where content is truncated
		do
			Result := buffer_size // 2
		end

	truncated_start_position: INTEGER_32
			-- Position in closing_content representing beginning of actual content
	
feature -- Status report

	is_truncated: BOOLEAN
			-- Has middle part of content been truncated?
		do
			Result := truncated_start_position > 0
		end

	Exists: BOOLEAN = False
			-- Does physical file exist?
			-- (Uses effective UID.)

	readable: BOOLEAN
			-- Is there a current item that may be read?
		do
			Result := writable
		end
	
feature -- Status setting

	wipe_out
			-- Empty buffer
		do
			buffer.wipe_out
			truncated_start_position := 0
		end
	
feature -- Output

	put_string (a_string: STRING_8)
			-- Append string to buffer.
			--
			-- a_string: String to be appended to buffer.
			-- Was declared in EQA_TEST_OUTPUT_BUFFER as synonym of putstring.
		local
			l_split, l_capacity, l_count, l_start, l_new_tsp: INTEGER_32
			l_ccount1: INTEGER_32
			l_chunk1, l_chunk2: STRING_8
		do
			if buffer.count + a_string.count > buffer_size then
				l_split := split_position
				if truncated_start_position = 0 then
					truncated_start_position := l_split + 1
					buffer.append (a_string.substring (1, buffer_size - buffer.count))
				end
				l_capacity := buffer_size - l_split
				if a_string.count > l_capacity then
					l_start := a_string.count - l_capacity + 1
					l_count := l_capacity
				else
					l_start := 1
					l_count := a_string.count
				end
				if truncated_start_position + l_count > buffer_size + 1 then
					l_ccount1 := buffer_size - truncated_start_position + 1
					l_new_tsp := l_split + 1 + l_count - l_ccount1
					l_chunk1 := a_string.substring (l_start, l_start + l_ccount1 - 1)
					buffer.replace_substring (l_chunk1, truncated_start_position, buffer.count)
					l_chunk2 := a_string.substring (l_start + l_ccount1, a_string.count)
					buffer.replace_substring (l_chunk2, l_split + 1, l_new_tsp - 1)
					truncated_start_position := l_new_tsp
				else
					buffer.replace_substring (a_string.substring (l_start, a_string.count), truncated_start_position, truncated_start_position + l_count - 1)
					if truncated_start_position + l_count > buffer_size then
						truncated_start_position := l_split + 1
					else
						truncated_start_position := truncated_start_position + l_count
					end
				end
			else
				buffer.append_string (a_string)
			end
		end

	putstring (a_string: STRING_8)
			-- Append string to buffer.
			--
			-- a_string: String to be appended to buffer.
			-- Was declared in EQA_TEST_OUTPUT_BUFFER as synonym of put_string.
		local
			l_split, l_capacity, l_count, l_start, l_new_tsp: INTEGER_32
			l_ccount1: INTEGER_32
			l_chunk1, l_chunk2: STRING_8
		do
			if buffer.count + a_string.count > buffer_size then
				l_split := split_position
				if truncated_start_position = 0 then
					truncated_start_position := l_split + 1
					buffer.append (a_string.substring (1, buffer_size - buffer.count))
				end
				l_capacity := buffer_size - l_split
				if a_string.count > l_capacity then
					l_start := a_string.count - l_capacity + 1
					l_count := l_capacity
				else
					l_start := 1
					l_count := a_string.count
				end
				if truncated_start_position + l_count > buffer_size + 1 then
					l_ccount1 := buffer_size - truncated_start_position + 1
					l_new_tsp := l_split + 1 + l_count - l_ccount1
					l_chunk1 := a_string.substring (l_start, l_start + l_ccount1 - 1)
					buffer.replace_substring (l_chunk1, truncated_start_position, buffer.count)
					l_chunk2 := a_string.substring (l_start + l_ccount1, a_string.count)
					buffer.replace_substring (l_chunk2, l_split + 1, l_new_tsp - 1)
					truncated_start_position := l_new_tsp
				else
					buffer.replace_substring (a_string.substring (l_start, a_string.count), truncated_start_position, truncated_start_position + l_count - 1)
					if truncated_start_position + l_count > buffer_size then
						truncated_start_position := l_split + 1
					else
						truncated_start_position := truncated_start_position + l_count
					end
				end
			else
				buffer.append_string (a_string)
			end
		end

	put_character (c: CHARACTER_8)
			-- Append character to buffer.
			--
			-- c: Character to be appended to buffer.
			-- Was declared in EQA_TEST_OUTPUT_BUFFER as synonym of putchar.
		local
			l_split, l_pos: INTEGER_32
		do
			if buffer.count + 1 > buffer_size then
				l_split := split_position
				if truncated_start_position = 0 then
					l_pos := l_split + 1
					truncated_start_position := l_split + 2
				else
					l_pos := truncated_start_position
					if truncated_start_position = buffer.count then
						truncated_start_position := l_split + 1
					else
						truncated_start_position := truncated_start_position + 1
					end
				end
				buffer.put (c, l_pos)
			else
				buffer.append_character (c)
			end
		end

	putchar (c: CHARACTER_8)
			-- Append character to buffer.
			--
			-- c: Character to be appended to buffer.
			-- Was declared in EQA_TEST_OUTPUT_BUFFER as synonym of put_character.
		local
			l_split, l_pos: INTEGER_32
		do
			if buffer.count + 1 > buffer_size then
				l_split := split_position
				if truncated_start_position = 0 then
					l_pos := l_split + 1
					truncated_start_position := l_split + 2
				else
					l_pos := truncated_start_position
					if truncated_start_position = buffer.count then
						truncated_start_position := l_split + 1
					else
						truncated_start_position := truncated_start_position + 1
					end
				end
				buffer.put (c, l_pos)
			else
				buffer.append_character (c)
			end
		end

	put_boolean (b: BOOLEAN)
			-- Write ASCII value of b at current position.
			-- Was declared in EQA_TEST_OUTPUT_BUFFER as synonym of putbool.
		do
			put_string (b.out)
		end

	putbool (b: BOOLEAN)
			-- Write ASCII value of b at current position.
			-- Was declared in EQA_TEST_OUTPUT_BUFFER as synonym of put_boolean.
		do
			put_string (b.out)
		end

	put_new_line
			-- Write a new line character at current position.
			-- Was declared in EQA_TEST_OUTPUT_BUFFER as synonym of new_line.
		do
			put_character ('%N')
		end

	new_line
			-- Write a new line character at current position.
			-- Was declared in EQA_TEST_OUTPUT_BUFFER as synonym of put_new_line.
		do
			put_character ('%N')
		end

	put_double (d: REAL_64)
			-- Write ASCII value d at current position.
			-- Was declared in EQA_TEST_OUTPUT_BUFFER as synonym of putdouble.
		do
			put_string (d.out)
		end

	putdouble (d: REAL_64)
			-- Write ASCII value d at current position.
			-- Was declared in EQA_TEST_OUTPUT_BUFFER as synonym of put_double.
		do
			put_string (d.out)
		end

	put_real (r: REAL_32)
			-- Write ASCII value of r at current position.
			-- Was declared in EQA_TEST_OUTPUT_BUFFER as synonym of putreal.
		do
			put_string (r.out)
		end

	putreal (r: REAL_32)
			-- Write ASCII value of r at current position.
			-- Was declared in EQA_TEST_OUTPUT_BUFFER as synonym of put_real.
		do
			put_string (r.out)
		end

	flush
			-- Flush buffered data to disk.
			-- Note that there is no guarantee that the operating
			-- system will physically write the data to the disk.
			-- At least it will end up in the buffer cache,
			-- making the data visible to other processes.
		do
		end
	
feature -- Basic functionality: file

	dispose
			-- Ensure this medium is closed when garbage collected.
		do
		end

	back
			-- Go back one position.
		do
		end

	end_of_file: BOOLEAN
			-- Has an EOF been detected?
		do
		end

	file_close (file: POINTER)
			-- Close file.
		do
		end
	
feature {NONE} -- Constants

	M_truncated: STRING_8 = "%N%N---------------------------%NTruncated section%N---------------------------%N%N"
	
invariant
	buffer_capacity_at_least_buffer_size: buffer.capacity >= buffer_size
	buffer_count_not_greater_buffer_size: buffer.count <= buffer_size
	valid_truncated_start_position: truncated_start_position <= buffer.count and (truncated_start_position = 0 or truncated_start_position > split_position)
	truncated_implies_buffer_full: is_truncated implies (buffer.count = buffer_size)

note
	copyright: "Copyright (c) 1984-2012, Eiffel Software and others"
	license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
	source: "[
		Eiffel Software
		5949 Hollister Ave., Goleta, CA 93117 USA
		Telephone 805-685-1006, Fax 805-685-6869
		Website http://www.eiffel.com
		Customer support http://support.eiffel.com
	]"

end -- class EQA_TEST_OUTPUT_BUFFER

Generated by ISE EiffelStudio