note
	description: "Common bahavior used in xml parsing"
	author: ""
	date: "$Date$"
	revision: "$Revision$"

deferred class
	EB_XML_CALLBACKS

inherit
	XML_CALLBACKS_FILTER
		redefine
			on_error,
			on_content,
			on_start_tag,
			on_attribute,
			on_start_tag_finish,
			on_end_tag
		end

	EB_XML_PARSE_HELPER

	EXCEPTIONS

	SHARED_BENCH_NAMES

feature{NONE} -- Initialization

	initialize
			-- Initialize.
		do
			initialize_state_transitions_tag
			initialize_tag_attributes
			initialize_processors
			initialize_attribute_name_table

			create current_tag.make
			create current_attributes.make (3)
			create current_content.make (256)
		end

feature -- Access

	last_error: EB_METRIC_ERROR
			-- Last reported error

feature -- Status report

	has_error: BOOLEAN
			-- Did error occur during xml parsing?
		do
			Result := last_error /= Void
		end

feature{NONE} -- Event handlers

	on_error (a_message: READABLE_STRING_32)
			-- Event producer detected an error.
		do
			create_last_error (a_message)
		end

	on_content (a_content: READABLE_STRING_32)
			-- Text content.
		do
			current_content.append (a_content)
		end

	on_start_tag (a_namespace: detachable READABLE_STRING_32; a_prefix: detachable READABLE_STRING_32; a_local_part: READABLE_STRING_32)
			-- Start of start tag.
		local
			l_tag: INTEGER
		do
			if current_tag.is_empty then
				current_tag.extend (t_none)
			end
			if attached state_transitions_tag.item (current_tag.item) as l_trans then
				l_tag := l_trans.item (a_local_part.to_string_32)
			end
			if l_tag = 0 then
				create_last_error (xml_names.err_invalid_tag_position (a_local_part))
			else
				current_tag.extend (l_tag)
			end
		end

	on_attribute (a_namespace: detachable READABLE_STRING_32; a_prefix: detachable READABLE_STRING_32; a_local_part: READABLE_STRING_32; a_value: READABLE_STRING_32)
			-- Start of attribute.
		local
			k_local: STRING_32
			l_attribute: INTEGER
			l_current_attributes: like current_attributes
		do
			if
				not a_local_part.is_case_insensitive_equal ("xmlns") and
				not	a_local_part.is_case_insensitive_equal ("xsi") and
				not a_local_part.is_case_insensitive_equal ("schemaLocation")
			then
					-- check if the attribute is valid for the current state
				if attached tag_attributes.item (current_tag.item) as l_attr then
					k_local := a_local_part.to_string_32
					k_local.to_lower
					l_attribute := l_attr.item (k_local)
				end
				l_current_attributes := current_attributes
				if l_current_attributes = Void then
					create l_current_attributes.make (1)
					current_attributes := l_current_attributes
				end
				if l_attribute /= 0 and then not l_current_attributes.has (l_attribute) then
					l_current_attributes.force (a_value, l_attribute)
				else
					create_last_error (xml_names.err_invalid_attribute (a_local_part))
				end
			end
		end

	on_start_tag_finish
			-- End of start tag.
		local
			l_tag: INTEGER
			l_start_prc: like tag_start_processors
		do
			l_tag := current_tag.item
			l_start_prc := tag_start_processors
			if l_start_prc.has (l_tag) then
				l_start_prc.item (l_tag).call (Void)
			end
			if attached current_attributes as l_current_attributes then
				l_current_attributes.wipe_out
			end
		end

	on_end_tag (a_namespace: detachable READABLE_STRING_32; a_prefix: detachable READABLE_STRING_32; a_local_part: READABLE_STRING_32)
			-- End tag.
		local
			l_tag: INTEGER
			l_finish_prc: like tag_finish_processors
		do
			l_tag := current_tag.item
			l_finish_prc := tag_finish_processors
			if l_finish_prc.has (l_tag) then
				l_finish_prc.item (l_tag).call (Void)
			end
			current_content.wipe_out
			current_tag.remove
		end

feature{NONE} -- Error handling

	create_last_error (a_message: READABLE_STRING_GENERAL)
			-- Create `last_error' with `a_message'.
			-- Raise an exception
		do
			create last_error.make (a_message)
			raise ("XML parser error")
		ensure then
			has_error: has_error
		end

feature{NONE} -- Implementation

	current_content: STRING_32
			-- Current content

	current_tag: LINKED_STACK [INTEGER]
			-- The stack of tags we are currently processing

	current_attributes: detachable HASH_TABLE [READABLE_STRING_32, INTEGER]
			-- The values of the current attributes	

	state_transitions_tag: HASH_TABLE [HASH_TABLE [INTEGER, STRING_32], INTEGER]
			-- Mapping of possible tag state transitions from `current_tag' with the tag name to the new state.

	tag_attributes: HASH_TABLE [HASH_TABLE [INTEGER, STRING_32], INTEGER]
			-- Mapping of possible attributes of tags.

	tag_start_processors: HASH_TABLE [PROCEDURE, INTEGER]
			-- Table of processors to be called when start tag finish is met.
			-- [processor, tag id]

	tag_finish_processors: HASH_TABLE [PROCEDURE, INTEGER]
			-- Table of processors to be called when tag finish is met.
			-- [processor, tag id]

	attribute_name_table: HASH_TABLE [READABLE_STRING_32, INTEGER]
			-- Table of attributes in form of [attribute_name, attribute_id]

	t_none: INTEGER
			-- Phony non node.
		deferred
		end

feature{NONE} -- Implementation

	initialize_state_transitions_tag
			-- Initialize `state_transitions_tag'.
		deferred
		end

	initialize_tag_attributes
			-- Initialize `tag_attributes'.
		deferred
		end

	initialize_processors
			-- Initialize processors for analysing nodes.
		deferred
		end

	initialize_attribute_name_table
			-- Initialize `attribute_name_table'.
		local
			l_attrs: like tag_attributes
			l_table: like attribute_name_table
			l_attr: like tag_attributes.item
		do
			create attribute_name_table.make (10)
			from
				l_table := attribute_name_table
				l_attrs := tag_attributes
				l_attrs.start
			until
				l_attrs.after
			loop
				l_attr := l_attrs.item_for_iteration
				from
					l_attr.start
				until
					l_attr.after
				loop
					l_table.force (l_attr.key_for_iteration, l_attr.item_for_iteration)
					l_attr.forth
				end
				l_attrs.forth
			end
		end

	retrieve_attribute_value (a_attr_id: INTEGER)
			-- Retrieve value of attribute whose id is `a_attr_id' from `current_attributes'.
			-- If succeeded, store last retrieved attribute value in `last_tested_attribute',
			-- otherwise raise an error.
		do
			check attribute_name_table.has (a_attr_id) end
			test_attribute (a_attr_id, current_attributes, agent create_last_error (xml_names.err_attribute_missing (attribute_name_table.item (a_attr_id))))
		end

invariant
	state_transitions_tag_attached: state_transitions_tag /= Void
	tag_attributes_attached: tag_attributes /= Void
	current_content_attached: current_content /= Void
	tag_start_processors_attached: tag_start_processors /= Void
	tag_finish_processors_attached: tag_finish_processors /= Void
	attribute_name_table_attached: attribute_name_table /= Void

note
	copyright: "Copyright (c) 1984-2012, 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