note
	description: "[
		Simpler writer for generating INI files on a medium ({IO_MEDIUM}) object.
		
		Note: Be sure to call `flush' at the end of writing to write any buffer content.
	]"
	legal: "See notice at end of class."
	status: "See notice at end of class."
	date: "$Date$"
	revision: "$Revision$"

class
	INI_PRINTER

create
	make

feature {NONE} -- Initialization

	make (a_medium: like medium)
			-- Initializes the write with a medium to write the generated contents to.
			--
			-- `a_medium': Medium to write generated contents to.
		require
			a_medium_attached: attached a_medium
			a_medium_is_open_write: attached {CONSOLE} a_medium or else a_medium.is_open_write
		do
			medium := a_medium

			create buffer.make (100)
		ensure
			medium_set: medium = a_medium
		end

feature -- Access

	medium: IO_MEDIUM
			-- Medium to write generated contents to.

feature {NONE} -- Access

	buffer: STRING
			-- Buffered content

feature -- Status report

	is_writable: BOOLEAN
			-- Indicates if the medium can be written to.
		do
			Result := attached {CONSOLE} medium or else medium.is_open_write
		ensure
			is_console_or_writable: Result implies (attached {CONSOLE} medium or else medium.is_open_write)
		end

	is_valid_section_name (a_name: READABLE_STRING_8): BOOLEAN
			-- Determines if a given section name is valid for an INI file.
			--
			-- `a_name': Section name to validate.
			-- `Result': True of the section name is valid; False otherwise.
		require
			a_name_attached: attached a_name
		local
			c: CHARACTER
			i, i_count: INTEGER
		do
			Result := a_name.count <= {INI_CONSTANTS}.max_name_length
			if Result then
				from
					i := 1
					i_count := a_name.count
				until
					i > i_count or else not Result
				loop
					c := a_name[i]
					Result := c.is_printable and c /= {INI_CONSTANTS}.section_start and c /= {INI_CONSTANTS}.section_end and c /= '%N'
					i := i + 1
				end
			end

		end

	is_valid_property_name (a_name: READABLE_STRING_8): BOOLEAN
			-- Determines if a given property name is valid for an INI file.
			--
			-- `a_name': Property name to validate.
			-- `Result': True of the property name is valid; False otherwise.
		require
			a_name_attached: attached a_name
		local
			c: CHARACTER
			i, i_count: INTEGER
		do
			i_count := a_name.count
			Result := i_count > 0 and then i_count <= {INI_CONSTANTS}.max_name_length
			if Result then
				from
					i := 1
				until
					i > i_count or else not Result
				loop
					c := a_name[i]
					Result := c.is_printable and c /= {INI_CONSTANTS}.property_value_qualifier and c /= '%N'
					i := i + 1
				end
			end
		end

	is_valid_property_value (a_value: READABLE_STRING_8): BOOLEAN
			-- Determines if a given property value is valid for an INI file.
			--
			-- `a_value': Property value to validate.
			-- `Result': True of the property name is valid; False otherwise.
		require
			a_value_attached: attached a_value
		local
			c: CHARACTER
			i, i_count: INTEGER
		do
			Result := True
			from
				i := 1
				i_count := a_value.count
			until
				i > i_count or else not Result
			loop
				c := a_value[i]
				Result := c.is_printable and c /= '%N'
				i := i + 1
			end
		end

feature {NONE} -- Status report

	is_spacing_required: BOOLEAN
			-- Indicates if any output has been produced already.
			--| Used for formatting.

feature -- Basic operations: Output

	put_new_line
			-- Puts an new line.
		require
			is_writable: is_writable
		do
			medium.new_line
			is_spacing_required := False

			flush_buffer
		end

	put_comment (a_comment: READABLE_STRING_8)
			-- Puts a new comment on the current line.
			--
			-- `a_comment': INI comment.
		require
			is_writable: is_writable
			a_comment_attached: attached a_comment
		local
			l_lines: LIST [READABLE_STRING_8]
			l_line: READABLE_STRING_8
			l_line_count: INTEGER
			l_buffer: like buffer
			l_spacing: BOOLEAN
		do
			l_spacing := is_spacing_required

			l_buffer := buffer
			l_lines := a_comment.split ('%N')
			from l_lines.start until l_lines.after loop
				l_buffer.append_character ({INI_CONSTANTS}.comment_pound)
				l_line := l_lines.item
				if not l_line.is_empty then
					l_buffer.append_character (' ')
					l_line_count := l_line.count
					if l_line[l_line_count] = '%R' then
						l_buffer.append_string_general (l_line.substring (1, l_line_count - 1))
					else
						l_buffer.append_string_general (l_line)
					end
				end
				l_buffer.append_character ('%N')
				l_lines.forth
			end

			is_spacing_required := l_spacing
		ensure
			is_spacing_required_unchanged: is_spacing_required = old is_spacing_required
		end

	put_section (a_name: READABLE_STRING_8)
			-- Puts a new INI section (e.g. [`a_name']) on the current line.
			--
			-- `a_name': Name of the section to write out.
		require
			is_writable: is_writable
			a_name_attached: attached a_name
			not_a_name_is_empty: not a_name.is_empty
			a_name_is_valid_section_name: is_valid_section_name (a_name)
		local
			l_medium: like medium
		do
			l_medium := medium
			if is_spacing_required then
				put_new_line
			end
			flush_buffer

			l_medium.put_character ({INI_CONSTANTS}.section_start)
			l_medium.put_string (a_name.as_string_8)
			l_medium.put_character ({INI_CONSTANTS}.section_end)
			put_new_line

			is_spacing_required := True
		ensure
			not_is_spacing_required: not is_spacing_required
		end

	put_property (a_name: READABLE_STRING_8; a_value: detachable READABLE_STRING_8)
			-- Puts a new INI property on the current line.
			-- Note: No section is required to put a property in an INI file.
			--
			-- `a_name': Name of the property to write out.
			-- `a_value': An optional value to associated with the property.
		require
			is_writable: is_writable
			a_name_attached: attached a_name
			not_a_name_is_empty: not a_name.is_empty
			a_name_is_valid_property_name: is_valid_property_name (a_name)
--			a_value_is_valid_property_value: attached a_value implies is_valid_property_value (a_value)
		local
			l_medium: like medium
		do
			flush_buffer

			l_medium := medium
			l_medium.put_string (a_name.as_string_8)
			l_medium.put_character (' ')
			l_medium.put_character ({INI_CONSTANTS}.property_value_qualifier)
			if attached a_value and then not a_value.is_empty then
				l_medium.put_character (' ')
				l_medium.put_string (a_value.as_string_8)
			end
			put_new_line

			is_spacing_required := True
		ensure
			is_spacing_required: is_spacing_required
		end

	flush
			-- Writes out any buffered content.
		require
			is_writable: is_writable
		do
			flush_buffer
			if attached {FILE} medium as l_file then
				l_file.flush
			end
		ensure
			buffer_is_empty: buffer.is_empty
		end

feature {NONE} -- Basic operations: Output

	flush_buffer
			-- Writes out the buffered content only.
			-- Note: Does not flush any disk content, for that use `flush'.
		require
			is_writable: is_writable
		do
			medium.put_string (buffer)
			buffer.wipe_out
		ensure
			buffer_is_empty: buffer.is_empty
		end

invariant
	medium_attached: attached medium
	is_writable: is_writable

;note
	copyright: "Copyright (c) 1984-2009, Eiffel Software"
	license: "GPL version 2 (see http://www.eiffel.com/licensing/gpl.txt)"
	licensing_options: "http://www.eiffel.com/licensing"
	copying: "[
			This file is part of Eiffel Software's Eiffel Development Environment.
			
			Eiffel Software's Eiffel Development Environment is free
			software; you can redistribute it and/or modify it under
			the terms of the GNU General Public License as published
			by the Free Software Foundation, version 2 of the License
			(available at the URL listed under "license" above).
			
			Eiffel Software's Eiffel Development Environment is
			distributed in the hope that it will be useful, but
			WITHOUT ANY WARRANTY; without even the implied warranty
			of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
			See the GNU General Public License for more details.
			
			You should have received a copy of the GNU General Public
			License along with Eiffel Software's Eiffel Development
			Environment; if not, write to the Free Software Foundation,
			Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
		]"
	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