note
	description: "[
		Used to modify Eiffel Configuration Files (ECF) by the way of adding assemblies and modifying
		existing assemblies.
	]"
	legal: "See notice at end of class."
	status: "See notice at end of class."
	author     : "$Author$"
	date       : "$Date$"
	revision   : "$Revision$"

class
	ECF_MODIFIER

create
	default_create

feature -- Basic Operations

	modify (a_fn: STRING; a_targets: LIST [STRING]; a_assemblies: LIST [LOCATED_ASSEMBLY])
			-- Modifies `a_fn' by adding the assemblies in `a_assemblies' to the ECF targets `a_targets' (or all targets if empty).
		require
			a_fn_attached: a_fn /= Void
			a_fn_exist: (create {RAW_FILE}.make (a_fn)).exists
			a_targets_attached: a_targets /= Void
			a_assemblies_attached: a_assemblies /= Void
			not_a_assemblies_is_empty: not a_assemblies.is_empty
		local
			l_doc: XML_DOCUMENT
			l_nodes: XML_NODE_LIST
			l_lists: ARRAYED_LIST [XML_NODE_LIST]
			l_cursor: CURSOR
			l_node: XML_ELEMENT
			l_errors: ARRAYED_LIST [STRING]
			l_count: INTEGER
			i: INTEGER
			retried: BOOLEAN
		do
			if not retried then
				create l_errors.make (0)
				create l_doc.make
				l_doc.load (a_fn)
				l_doc.save (a_fn + ".bak")

				create l_lists.make (5)
				if a_targets = Void or else a_targets.is_empty then
						-- Add node list for all targets
					l_nodes := l_doc.document_element.select_nodes ("child::target")
					if l_nodes.count > 0 then
						l_lists.extend (l_nodes)
					end
				else
						-- Add node list for specified targets
					l_cursor := a_targets.cursor
					from a_targets.start until a_targets.after loop
						l_nodes := l_doc.document_element.select_nodes ("child::target[@name=%"" + a_targets.item + "%"]")
						if l_nodes.count > 0 then
							l_lists.extend (l_nodes)
						else
							l_errors.extend ("The specified target '" + a_targets.item + "' does not exist in the specified ECF file.")
						end
						a_targets.forth
					end
					a_targets.go_to (l_cursor)
				end

				from l_lists.start until l_lists.after loop
					l_nodes := l_lists.item
					l_count := l_nodes.count
					from i := 0 until i = l_count loop
						l_node ?= l_nodes.item (i)
						check l_node_attached: l_node /= Void end
						if l_node /= Void then
							add_assemblies (l_doc, l_node, a_assemblies)
						end
						i := i + 1
					end
					l_lists.forth
				end
				if l_errors.is_empty then

				end
				l_doc.save (a_fn)
			else
				l_errors.extend ("File '" + "' could not be modified. Please check you have the correct permission and it is a valid ECF file.")
			end
			errors := l_errors
		ensure
			a_targets_unmoved: a_targets.cursor.is_equal (old a_targets.cursor)
			a_assemblies_unmoved: a_assemblies.cursor.is_equal (old a_assemblies.cursor)
		rescue
			retried := True
			retry
		end

feature -- Access

	errors: LIST [STRING]
			-- List of error generated during modification

feature -- Status report

	successful: BOOLEAN
			-- Indicates if modification was successful
		do
			Result := errors = Void or else errors.is_empty
		end

feature {NONE} -- Modification implementation

	add_assemblies (a_doc: XML_DOCUMENT; a_node: XML_ELEMENT; a_assemblies: LIST [LOCATED_ASSEMBLY])
			-- Add assemblies `a_assemblies' to XML node `a_node'.
		local
			l_cursor: CURSOR
			l_name: SYSTEM_STRING
			l_elm: XML_ELEMENT
			l_atr: XML_ATTRIBUTE
			l_nodes: XML_NODE_LIST
			l_prefix: SYSTEM_STRING
			l_count: INTEGER
			i: INTEGER
		do
			l_cursor := a_assemblies.cursor
			from a_assemblies.start until a_assemblies.after loop
				l_prefix := Void
				l_name := a_assemblies.item.assembly.get_name.name.to_lower

					-- Remove existing nodes and retrieve a prefix, if any
				l_nodes := a_node.select_nodes ("child::assembly[@name=%"" + create {STRING}.make_from_cil (l_name) + "%"]")
				l_count := l_nodes.count
				from i := 0 until i = l_count loop
					l_elm ?= l_nodes.item (i)
					l_atr := l_elm.get_attribute_node ("prefix")
					if l_atr /= Void then
						l_prefix := l_atr.value
					end
					l_elm ?= a_node.remove_child (l_elm)
					i := i + 1
				end
				l_elm := a_doc.create_element ("assembly")

				l_atr := a_doc.create_attribute ("name")
				l_atr.set_value (l_name)
				l_atr := l_elm.attributes.append (l_atr)

				l_atr := a_doc.create_attribute ("location")
				l_atr.set_value (location_for_assembly (a_assemblies.item))
				l_atr := l_elm.attributes.append (l_atr)

				if l_prefix /= Void and then l_prefix.length > 0 then
					l_atr := a_doc.create_attribute ("prefix")
					l_atr.set_value (l_prefix)
					l_atr := l_elm.attributes.append (l_atr)
				end

				l_elm ?= a_node.append_child (l_elm)
				a_assemblies.forth
			end
			a_assemblies.go_to (l_cursor)
		ensure
			a_assemblies_unmoved: a_assemblies.cursor.is_equal (old a_assemblies.cursor)
		end

feature -- Query

	location_for_assembly (a_assembly: LOCATED_ASSEMBLY): STRING
			-- Retrieve a formatted location for assembly `a_assembly'
		require
			a_assembly_attached: a_assembly /= Void
		local
			l_loc: STRING
			l_fw_path: STRING
			l_len: INTEGER
		do
			l_loc := a_assembly.real_path.as_lower
			l_fw_path := {RUNTIME_ENVIRONMENT}.get_runtime_directory.to_lower
			l_len := l_fw_path.count
			if l_loc.count >= l_len and then l_fw_path.is_equal (l_loc.substring (1, l_len)) then
					-- Replace with ISE_DOTNET_FRAMEWORK
				l_loc.replace_substring (once "${ISE_DOTNET_FRAMEWORK}\", 1, l_len)
			end
			Result := l_loc
		ensure
			result_attached: Result /= Void
			not_result_is_empty: not Result.is_empty
		end

invariant
	errors_attached: not successful implies errors /= Void
	not_errors_is_empty: not successful implies not errors.is_empty

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 -- class {ECF_MODIFIER}