indexing
	description: "Class for parsing XML documents"
	status: "See notice at end of class."
	author: "Andreas Leitner"

deferred class
	XML_PARSER_ABS
inherit
	EXPAT_ERROR_CODES
	EXPAT_EXTERNALS
		rename
			get_base as get_base_expat,
			set_base as set_base_expat
		end
	EXML_EXTERNALS

feature {NONE}  -- Initialisation
	make is
		do
			item := parser_create (default_pointer)

			exml_register_start_end_tag_hook (item)
			exml_register_content_hook (item)
			exml_register_processing_instruction_hook (item)
		--	exml_register_default_hook (item)
				-- registering the default hook, prevents the parser
				-- from expanding internal  entity-references
			exml_register_unparsed_entity_declaration_hook (item)
			exml_register_notation_declaration_hook (item)
			exml_register_external_entity_reference_hook (item)
			exml_register_unkown_encoding (item)

			set_eiffel_object_as_user_data
		end


feature {ANY} -- operations

	parse_string (data: STRING) is
			-- Parse 'data'.
			-- Note: You can call parse_string multiple times
			-- and give the parse the document in parts only.
			-- You have to call 'set_end_of_file' after the
			-- last call to 'parse_string' in every case.
		require
			data_not_void: data /= Void
		do
			if
				parse_string_imp (data) = 0
					-- function call with side effect !!!!
			then
				last_error := get_error_code (item)	
			end
		end

	set_end_of_file is
			-- Call this routine to
			-- tell the parser that the document has been
			-- completly parsed and no input is comming anymore.
		local
			int_result: INTEGER
			a: ANY
			s: STRING
		do
			int_result := parse (item, default_pointer, 0, 1)

			if
				int_result = 0
			then
				last_error := get_error_code (item)	
			end
		end

feature -- Access
	last_error: INTEGER
			-- see XML_PARSER_ERROR_CODES

	is_correct: BOOLEAN is
			-- returns False if an error was detected
			-- by the parser
		do
			Result := last_error = Xml_error_none
		ensure
			error_flag_set: (Result = True) implies (last_error = Xml_error_none)
			error_flag_set2: (Result = False) implies (last_error /= Xml_error_none)
		end


	last_error_description: STRING is deferred end
			-- gives a text that explain 'last_error'

	last_error_extended_description: STRING is
			-- extended description of 'last_error'
		do
			!! Result.make (0)
			Result.append (last_error_description)
			Result.append ("(")
			Result.append (last_error.out)
			Result.append (") at ln: ")
			Result.append (last_line_number.out)
			Result.append (", cl: ")
			Result.append (last_column_number.out)
		end

	last_line_number: INTEGER is deferred end
			-- current line number (works in callback too)
	last_column_number: INTEGER is deferred end
			-- current column number (works in callback too)
	last_byte_index: INTEGER is deferred end
			-- current byte index (works in callback too)

	set_base (a_base: STRING) is deferred end
			-- sets the base to be used for resolving URIs in system identifiers
			-- in declarations. 
	get_base: STRING is deferred end
			-- returns the base

feature {NONE} -- Redefinable Callbacks
	on_start_tag (start_tag: XML_START_TAG) is
			-- called whenever the parser findes a start element
		require
			start_tag_not_void: start_tag /= Void
		do
		end

	on_content (content: XML_CONTENT) is
			-- called whenever the parser findes character data
		require
			content_not_void: content /= Void
		do
		end

	on_end_tag (end_tag: XML_END_TAG) is
			-- called whenever the parser findes an end element
		require
			end_tag_not_void: end_tag /= Void
		do
		end
	on_default (data: CHARACTER_ARRAY) is
		-- this feature is called for any character in the XML
		-- document for which there is no applicable handler
	
		require
			data_not_void: data /= Void
		do
		end

feature {NONE} -- Implementation

	pass_to_default_handler is
			-- can be called within a start, end, processing instruction,
			-- and content handler to pass the current markup to the default handler
			-- TODO: work out dbc to ensure it is called only from the
			-- good features!
		do
			pass_to_defaul_handler (item)
		end
	
feature {NONE} -- (low level) frozen callbacks (called from eXML clib)

	frozen on_start_tag_procedure (tag_name_ptr, attribute_specifications_ptr: POINTER) is
		require
			tag_name_ptr_not_void: tag_name_ptr /= Void
			attribute_specifications_ptr_not_void: attribute_specifications_ptr /= Void
		local
			start_tag: XML_START_TAG
		do
			!! start_tag.make_from_c (tag_name_ptr, attribute_specifications_ptr)
			on_start_tag (start_tag)
		end

	frozen on_end_tag_procedure (tag_name_ptr: POINTER) is
		local
			end_tag: XML_END_TAG
		do
			!! end_tag.make_from_c (tag_name_ptr)
			on_end_tag (end_tag)
		end

	frozen on_content_procedure (content_ptr: POINTER; len: INTEGER) is
		local
			content: XML_CONTENT
		do
			!! content.make_from_c (content_ptr, len)
			on_content (content)
		end

	frozen on_processing_instruction_procedure (target, data: POINTER) is
		do
			print ("found processing_instruction (HELP!!!)%N")
				-- TODO: Don't really know yet. If you know please let me know too (:
		end

	frozen on_default_procedure_address (data_ptr: POINTER; len: INTEGER) is
		local
			data: CHARACTER_ARRAY
		do
			!! data.make_from_c (data_ptr, len)
			on_default (data)
		end

	frozen on_unparsed_entity_declaration_procedure (enitity_name, base, system_id, public_id, notation_name: POINTER) is
		do
			print ("found unparsed_entity_declaration (HELP!!!)%N")
				-- TODO: Don't really know yet. If you know please let me know too (:
	
		end
	frozen on_notation_declaration_procedure (notation_name, base, system_id, public_id: POINTER) is
		do
			print ("found notation_declaration (HELP!!!)%N")
				-- TODO: Don't really know yet. If you know please let me know too (:
		end

	frozen on_external_entity_reference_procedure (open_entity_name, base, system_id, public_id: POINTER): INTEGER is
			-- return zero if parsing of external entity was not successfull
			-- 'base' is a char* pointing to data previously set with 'set_base'
		do
			print ("found external_entity_reference (HELP!!!)%N")
			Result := 1
				-- TODO: Well,, I guess I need to start another parser here - always?
				-- maybe this should be configurable
				-- anyway there needs to be some expat stuff encapsulated first (XML_ExternalEntityParserCreate)
				-- this function is used to create a parse, that parses those external references.
				-- I assume this will be a heir from XML_PARSER.
		end

	frozen on_unkown_encoding_procedure (name, info: POINTER): INTEGER is
		do
			print ("found unkown_encoding (HELP!!!)%N")
				-- TODO: Don't really know yet. If you know please let me know too (:
		end

feature {NONE}

	parse_string_imp  (data: STRING): INTEGER is
			-- function has side effect !!!
		require
			data_not_void: data /= Void
		deferred 
		end

	set_eiffel_object_as_user_data is deferred end
			-- expat hooks receive a user defineble integer as
			-- additional parameter. we set this to the Eiffel
			-- parser object.

	item: POINTER
		-- the expat parser handle

	

invariant
	item_not_void: item /= Void
end	-- XML_PARSER_ABS
--|-------------------------------------------------------------------------
--| eXML, Eiffel XML Parser Toolkit
--| Copyright (C) 1999  Andreas Leitner
--| See the file forum.txt included in this package for licensing info.
--|
--| Comments, Questions, Additions to this library? please contact:
--|
--| Andreas Leitner
--| Arndtgasse 1/3/5
--| 8010 Graz
--| Austria
--| email: andreas.leitner@teleweb.at
--| web: http://exml.dhs.org
--|-------------------------------------------------------------------------