class EL_APPLICATION_IMPLEMENTATION

(source code)

description

Implementation details for class EL_APPLICATION

note
	description: "Implementation details for class ${EL_APPLICATION}"

	author: "Finnian Reilly"
	copyright: "Copyright (c) 2001-2022 Finnian Reilly"
	contact: "finnian at eiffel hyphen loop dot com"

	license: "MIT license (See: en.wikipedia.org/wiki/MIT_License)"
	date: "2024-09-25 11:10:41 GMT (Wednesday 25th September 2024)"
	revision: "2"

deferred class
	EL_APPLICATION_IMPLEMENTATION

inherit
	EL_MODULE_BUILD_INFO; EL_MODULE_EXCEPTION; EL_MODULE_EXECUTABLE; EL_MODULE_DIRECTORY
	EL_MODULE_FILE_SYSTEM; EL_MODULE_LIO; EL_MODULE_OS_RELEASE; EL_MODULE_OS

	EL_SHARED_BASE_OPTION; EL_SHARED_APPLICATION_OPTION; EL_SHARED_SOFTWARE_VERSION

	EL_CHARACTER_32_CONSTANTS

feature -- Status query

	is_legacy_app: BOOLEAN
		-- `True' if application potentially has users with app directories in legacy locations
		-- prior to April 2020. See `{EL_STANDARD_DIRECTORY_I}.Legacy_table'
		do
			Result := False
		end

	is_switched: BOOLEAN
		-- `True' if current app is reached by a command line switch detected in class `EL_MULTI_APPLICATION_ROOT'
		-- which implies this is not the root class

	is_root_class: BOOLEAN
		do
			Result := not is_switched
		end

feature {EL_APPLICATION} -- Factory routines

	new_command_options: EL_APPLICATION_COMMAND_OPTIONS
		do
			create Result.make
		end

	new_configuration: detachable EL_APPLICATION_CONFIGURATION
		-- redefine to create configuration singleton just before `initialization' routine is called
		do
		end

	new_option_name: ZSTRING
		do
			create Result.make_from_general (option_name)
		end

feature {NONE} -- Implementation

	call (object: ANY)
			-- For initializing once routines
		do
		end

	create_app_directories
		local
			option_sub_dir, dir_path: DIR_PATH
		do
			if is_legacy_app then
				migrate_legacy_directories
			end
			create option_sub_dir.make (option_name)
			across App_directory_list as list loop
				if is_switched then
					dir_path := list.item.plus_dir (option_sub_dir)
				else
					dir_path := list.item
				end
				if not dir_path.exists then
					File_system.make_directory (dir_path)
				end
			end
		end

	migrate (legacy_dir, standard_dir: DIR_PATH)
		-- migrate legacy paths to standard
		require
			legacy_dir_exists: legacy_dir.exists
		do
			File_system.make_directory (standard_dir.parent)
			OS.move_to_directory (legacy_dir, standard_dir.parent)
			File_system.delete_if_empty (legacy_dir.parent)
		end

	migrate_legacy_directories
		do
			if attached Directory.Legacy_table as table then
				across App_directory_list as list loop
				-- If a differing legacy data directory exists already, move it to standard location
					if attached list.item as standard_dir and then table.has_key (standard_dir)
						and then attached table.found_item as legacy_dir
						and then legacy_dir.exists and then legacy_dir /~ standard_dir
					then
						migrate (legacy_dir, standard_dir)
					end
				end
			end
		end

	on_operating_system_signal
			--
		do
		end

	read_command_options
		-- read command line options
		do
		end

	return_to_quit
		do
			lio.put_new_line
			io.put_string ("<RETURN TO QUIT>")
			io.read_character
		end

	show_benchmarks (timer: EL_EXECUTION_TIMER)
		-- show execution times and average execution time since last version update
		local
			timer_data: RAW_FILE; data_version: NATURAL; i, data_count: INTEGER
			sum_elapsed_times: DOUBLE; file_path: FILE_PATH
		do
			file_path := Directory.Sub_app_data + "show_benchmarks.dat"
			timer.stop
			across (";Average ").split (';') as l_prefix loop
				lio.put_labeled_string (l_prefix.item + "Execution time", timer.elapsed_time.out)
				lio.put_new_line
				if l_prefix.is_first then
					-- Set average elapsed time from previous runs
					if file_path.exists then
						create timer_data.make_open_read (file_path)
						timer_data.read_natural
						data_version := timer_data.last_natural
						data_count := (timer_data.count - {PLATFORM}.Natural_32_bytes) // {PLATFORM}.Real_64_bytes
						from i := 1 until i > data_count loop
							timer_data.read_double
							sum_elapsed_times := sum_elapsed_times + timer_data.last_double
							i := i + 1
						end
					else
						File_system.make_directory (file_path.parent)
						create timer_data.make_open_write (file_path)
						timer_data.put_natural_32 (Build_info.version_number)
						data_version := Build_info.version_number
					end
					timer_data.close
					if Build_info.version_number > data_version then
						-- Reset file to zero items
						create timer_data.make_open_write (file_path)
						timer_data.put_natural_32 (Build_info.version_number)
						sum_elapsed_times := 0; data_count := 0
					else
						create timer_data.make_open_append (file_path)
					end
					timer_data.put_double (timer.elapsed_millisecs)
					timer_data.close
					lio.put_integer_field ("Previous runs", data_count)
					lio.put_new_line
					sum_elapsed_times := sum_elapsed_times + timer.elapsed_millisecs
					data_count := data_count + 1
					timer.set_elapsed_millisecs ((sum_elapsed_times / data_count).rounded)
				end
			end
		end

	visible_types: TUPLE
		-- types with lio output visible in console
		-- See: {EL_CONSOLE_MANAGER_I}.show_all
		do
			create Result
		end

	visible_types_list: EL_TUPLE_TYPE_LIST [EL_MODULE_LIO]
		do
			create Result.make_from_tuple (visible_types)
		ensure
			all_conform_to_EL_MODULE_LIO: Result.all_conform
		end

feature {NONE} -- Deferred

	option_name: READABLE_STRING_GENERAL
		-- command line switch option name
		deferred
		end

feature {NONE} -- Internal attributes

	internal_timer: detachable EL_EXECUTION_TIMER

feature {EL_DESKTOP_ENVIRONMENT_I} -- Constants

	App_directory_list: EL_ARRAYED_LIST [DIR_PATH]
		once
			Result := Directory.app_list
		end

	Environment_variable: EL_DEFINE_VARIABLE_COMMAND_OPTION
		-- Define an environment variable: -define name=<value>
		-- To use, add this to redefinition of `standard_options' in descendant
		once
			create Result.make
		end

end