note
	description: "[
		Provides access to application specific options for use with initializing applications or processing data.
	]"
	legal: "See notice at end of class."
	status: "See notice at end of class.";
	date: "$Date$";
	revision: "$Revision $"

class
	APPLICATION_OPTIONS

inherit
	ANY

	SHARED_STATUS_MESSAGES
		export
			{NONE} all
		end

create
	make

feature {NONE} -- Initialization

	make (a_params: IAPPLICATION_PARAMETERS)
			-- Initialize application options using application parameters `a_params'.
		require
			a_params_attached: a_params /= Void
			a_params_is_readable: a_params.is_readable
		local
			l_name: STRING
			l_stub: BOOLEAN
			l_interface: BOOLEAN
			l_both: BOOLEAN
		do
			l_stub := a_params.generate_stub
			l_interface := a_params.generate_interface
			l_both := l_stub and l_interface

				-- Set class name and output file name
			if a_params.has_class_name then
				l_name := a_params.class_name.as_upper
			else
				l_name := default_class_name
			end

			if l_stub then
				stub_class_name := l_name.as_upper
				stub_class_name.append (once "_IMPL")
				stub_output_file_name := stub_class_name.as_lower
				stub_output_file_name.append (eiffel_file_extension)
			end
			interface_class_name := l_name.as_upper
			interface_output_file_name := l_name.as_lower
			interface_output_file_name.append (eiffel_file_extension)

				-- Set generation options
			generate_stub := l_stub
			generate_interface := l_interface
			generate_process_routines := a_params.generate_process_routines

				-- Set class name
			if a_params.use_user_data then
				user_data_class_name := a_params.user_data_class_name
			end

				-- Calculate files
			calcuate_files (a_params)

			create universe.make (files)
		end

feature -- Access

	universe: APPLICATION_UNIVERSE

	files: LIST [STRING_8]
			-- Files to include in generation of factory

	interface_class_name: STRING
			-- Class name for interface class

	stub_class_name: STRING
			-- Class name for stub class

	stub_output_file_name: STRING
			-- Path to generate stub output file

	interface_output_file_name: STRING
			-- Path to generate interface output file

	user_data_class_name: STRING
			-- User data class name

	generate_stub: BOOLEAN
			-- Indicates if a stub class should be generated

	generate_interface: BOOLEAN
			-- Indicates if an interface class should be generated

	generate_process_routines: BOOLEAN
			-- Should we generate `process' routines for each class being added to the visitor?

feature -- Status report

	use_user_data: BOOLEAN
			-- Indiciates if user data should be passed via a process feature call
		once
			Result := user_data_class_name /= Void
		end

feature {NONE} -- Query

	has_eiffel_extension (a_file_name: STRING): BOOLEAN
			-- Determines if `a_file_name' has an Eiffel source file extension
		require
			a_file_name_not_void: a_file_name /= Void
			not_a_file_name_is_empty: not a_file_name.is_empty
		local
			l_count: INTEGER
		do
			l_count := a_file_name.count
			Result := l_count > 2 and then a_file_name.substring (l_count - 1, l_count).is_case_insensitive_equal (eiffel_file_extension)
		end

feature {NONE} -- Implementation

	calcuate_files (a_params: IAPPLICATION_PARAMETERS)
			-- Calculates requested for generation by `a_params'
		require
			a_params_attached: a_params /= Void
			a_params_successful: a_params.is_readable
		local
			l_exclude_rx: detachable RX_PCRE_MATCHER
			l_list: LIST [STRING]
			l_excludes: LIST [STRING]
			l_array_list: ARRAYED_LIST [STRING]
			l_files: ARRAYED_LIST [STRING]
			l_cursor: CURSOR
			l_recurse: BOOLEAN
		do
			l_list := a_params.included_files
			if l_list.is_empty then
				if attached {STRING} (create {EXECUTION_ENVIRONMENT}).current_working_directory as l_wd then
					create l_array_list.make (1)
					l_array_list.extend (l_wd)
					l_list := l_array_list
				else
					check this_is_bug: False end
				end
			end

			l_excludes := a_params.exclude_expressions
			if not l_excludes.is_empty then
				l_exclude_rx := create_pattern_for_file_list (l_excludes)
			end
			l_recurse := a_params.recurse_directories

				-- Retrieve all files
			create l_files.make (10)
			files := l_files
			l_cursor := l_list.cursor
			from l_list.start until l_list.after loop
				append_file_list (l_list.item, l_files, l_recurse, l_exclude_rx)
				l_list.forth
			end
			l_list.go_to (l_cursor)
		ensure
			files_attached: files /= Void
		end

	append_file_list (a_path: STRING; a_list: ARRAYED_LIST [STRING]; a_recurse: BOOLEAN; a_expression: detachable RX_PCRE_MATCHER)
			-- Appends Eiffel source files located at or in `a_path' to `a_list'. If `a_path' happens to
			-- be a directory and `a_recurse' is True then any subdirectories will also be scanned.
			-- `a_expression' represents an excluded expression
		require
			a_path_not_void: a_path /= Void
			a_list_not_void: a_list /= Void
			not_a_path_is_empty: not a_path.is_empty
		local
			l_file: RAW_FILE
			l_directory: KL_DIRECTORY
		do
			create l_file.make (a_path)
			if l_file.exists then
				if l_file.is_directory or l_file.is_device then
					create l_directory.make (a_path)
					l_directory.filenames.do_all (agent (ia_item: STRING; ia_inner_list: ARRAYED_LIST [STRING]; ia_dir: STRING; ia_in_expr: detachable RX_PCRE_MATCHER)
						require
							ia_item_not_void: ia_item /= Void
							ia_inner_list_not_void: ia_inner_list /= Void
							ia_dir_not_void: ia_dir /= Void
							not_ia_item_is_empty: not ia_item.is_empty
						local
							l_file_name: FILE_NAME
						do
							if has_eiffel_extension (ia_item) then
								if not ia_dir.is_empty then
									create l_file_name.make_from_string (ia_dir)
								else
									create l_file_name.make
								end
								l_file_name.set_file_name (ia_item)
								if ia_in_expr = Void or else not ia_in_expr.matches (l_file_name) then
									ia_inner_list.extend (l_file_name)
								end
							end
						end (?, a_list, a_path, a_expression))

					if a_recurse then
						l_directory.directory_names.do_all (agent (ia_item: STRING; ia_files: ARRAYED_LIST [STRING]; ia_dir: STRING; ia_in_expr: detachable RX_PCRE_MATCHER)
							require
								ia_item_not_void: ia_item /= Void
								ia_files_not_void: ia_files /= Void
								ia_dir_not_void: ia_dir /= Void
								not_ia_item_is_empty: not ia_item.is_empty
							local
								l_path_name: FILE_NAME
							do
								if not ia_dir.is_empty then
									create l_path_name.make_from_string (ia_dir)
								else
									create l_path_name.make
								end
								l_path_name.set_file_name (ia_item)
								if ia_in_expr = Void or else not ia_in_expr.matches (l_path_name) then
									append_file_list (l_path_name, ia_files, True, ia_in_expr)
								end
							end (?, a_list, a_path, a_expression))
					end
				else
					if has_eiffel_extension (a_path) then
						a_list.extend (a_path)
					end
				end
			end
		end

	create_pattern_for_file_list (a_list: LIST [STRING]): RX_PCRE_MATCHER
			-- Creates an regular expression pattern for list `a_list' and returns the result
		require
			a_list_attached: a_list /= Void
			not_a_list_is_empty: not a_list.is_empty
		local
			l_pattern: STRING
			l_cursor: CURSOR
		do
			l_cursor := a_list.cursor
			create l_pattern.make (1024)
			from a_list.start until a_list.after loop
				if not a_list.isfirst then
					l_pattern.append_character ('|')
				end
				l_pattern.append_character ('(')
				l_pattern.append (a_list.item)
				l_pattern.append_character (')')
				a_list.forth
			end
			a_list.go_to (l_cursor)
			create Result.make
			Result.compile (l_pattern)
		ensure
			result_attached: Result /= Void
			result_is_compiled: Result.is_compiled
			a_list_unmoved: a_list.cursor.is_equal (old a_list.cursor)
		end

feature {NONE} -- Contants

	eiffel_file_extension: STRING = ".e"
			-- Eiffel source file extension

	default_output_file_name: STRING = "generated_visitor.e"
			-- Default file name for output file

	default_class_name: STRING = "GENERATED_VISITOR"
			-- Default class name for output file

invariant
	files_attached: files /= Void
	generate_stub_and_or_interface: generate_stub or generate_interface

	stub_class_name_name_attached: generate_stub implies stub_class_name /= Void
	not_stub_class_name_is_empty: stub_class_name /= Void implies not stub_class_name.is_empty

	interface_class_name_name_attached: interface_class_name /= Void
	not_interface_class_name_is_empty: not interface_class_name.is_empty

	stub_output_file_name_attached: generate_stub implies stub_output_file_name /= Void
	not_stub_output_file_name_is_empty: stub_output_file_name /= Void implies not stub_output_file_name.is_empty

	interface_output_file_name_attached: generate_interface implies interface_output_file_name /= Void
	not_interface_output_file_name_is_empty: interface_output_file_name /= Void implies not interface_output_file_name.is_empty

	user_data_class_name_attached: use_user_data implies user_data_class_name /= Void
	not_user_data_class_name_is_empty: user_data_class_name /= Void implies not user_data_class_name.is_empty


;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 -- class {APPLICATION_OPTIONS}