indexing
	cluster: "demangler"
	namespace: "candidate"
	description: "Software representation of file system tree."
	original_author: "Justin Finnerty"
	original_date: "July 2005"
	current_date: "$Date: 2005/10/31 07:18:05 $"
	current_author: "$Author: finnerty $"
	revision: "$Revision: 2.5 $"	
	-- $Log: synchronised_directory.ge,v $
	-- Revision 2.5  2005/10/31 07:18:05  finnerty
	-- Added ability to create symlinks between directories as an alternative to merging. The major changes are in DIRDUP_REPORT with others adding/changing features as required.
	--
	-- Revision 2.4  2005/10/30 03:40:59  finnerty
	-- Split REPORT_VIEW into REPORT_CONTROL and REPORT_VIEW. Made changes to other classes as necessary
	--
	-- Revision 2.3  2005/09/30 06:48:16  finnerty
	-- Finalised for alpha release with SE and VE compilers
	--
	-- Revision 2.2  2005/09/27 06:08:51  finnerty
	-- Corrected variable name
	--
	-- Revision 2.1  2005/09/06 23:01:14  finnerty
	-- Renamed *.e to *.ge for gepp
	--
	-- Revision 2.1  2005/09/02 07:34:31  finnerty
	-- Moved some features into class from descendents
	--
	-- Revision 2.0  2005/08/26 23:18:23  finnerty
	-- == alpha version ==
	--
	-- Revision 1.3  2005/08/21 11:57:39  finnerty
	-- Alpha 1 version
	--
	-- Revision 1.2  2005/08/17 06:43:16  finnerty
	-- Updated to support TRANSFORM sub-type operations.
	--
	-- Revision 1.1  2005/08/12 06:53:13  finnerty
	-- Initial version of abstract data type for a synchronised directory
			--
	
deferred class SYNCHRONISED_DIRECTORY
	-- Software representation of a file system tree.

feature {ANY}
	-- Queries on the type of object
	is_peer: BOOLEAN
			-- Is this working directory?

	is_store: BOOLEAN is
			-- Is this back-up directory?
		do
			Result := not is_peer
		end

	namelist: DS_SPARSE_TABLE[DS_LIST[FILE_SYSTEM_OBJECT],STRING]
			-- [FSO:filename] dictionary

	reports: DS_LIST[SYNC_REPORT]
			-- Reports to fix file system tree

	root: FILE_SYSTEM_OBJECT
			-- Root of the tree.

	to_delete_base: FILE_SYSTEM_OBJECT
			-- The place were unwanted FSOs go.
	
feature {ANY}
	-- Manipulate the reports
	frozen do_reports is
			-- Generate and view each report in collection on
			-- directory. (template method)
		require
			Valid_root: root /= Void and to_delete_base /= Void
			Valid_report: not reports.is_empty
			Valid_dir: not namelist.is_empty
			Valid_control: has_controller
		do
			-- Start logging
			if log = Void then
				log_start ("LOG")
			end
			from reports.start until reports.off
			loop
				do_report (reports.item_for_iteration)
				reports.forth
			end
			-- Stop logging
			log_stop
			-- Remove trash directory should really go here...
			if not to_delete_base.has_children then
				remove_empty_trash_directory
			end
		end

	has_controller: BOOLEAN is
			-- Do we have a control object?
		do
			Result := m_controller /= Void
		ensure
			Result implies m_controller /= Void
		end

feature {ANY}
	-- Manipulate the file system
	does_path_exist (a_parent_path, a_name: STRING): BOOLEAN is
			-- Is there a real file?
		require
			a_parent_path /= Void and then not a_parent_path.is_empty
			-- a_name may be Void
		deferred
		end

	is_name_valid (a_parent_path, a_name: STRING): BOOLEAN is
			-- Can we combine the two to get a valid filename?
		require
			a_parent_path /= Void and then not a_parent_path.is_empty
			-- a_name may be Void
		deferred
		end

	remove_empty_trash_directory is
			-- If the trash directory is empty, delete the real trash 
			-- directory.
		require
			not to_delete_base.has_children
		deferred
		end

	rename_to (a_parent_path, a_name, an_new_name: STRING) is
			-- Rename a file system object `an_name' in directory
			-- `a_parent_path' to `an_new_name'.
		require
			a_parent_path /= Void and then not a_parent_path.is_empty
			a_name /= Void and then not a_name.is_empty
			an_new_name /= Void and then not an_new_name.is_empty
			Name_is_valid: is_name_valid (a_parent_path, a_name)
								is_name_valid (a_parent_path, an_new_name)
			Path_exists: does_path_exist (a_parent_path, a_name)
			No_overwrite: not does_path_exist (a_parent_path, an_new_name)
		deferred
		ensure
			Path_exists: does_path_exist (a_parent_path, an_new_name)
			Moved: not does_path_exist (a_parent_path, a_name)
		end

	move_to (a_old_path, a_new_path: STRING) is
			-- Move file system object to new path; Assumes real change
			-- in parent path.
		require
			a_old_path /= Void and then not a_old_path.is_empty
			a_new_path /= Void and then not a_new_path.is_empty
			Path_exists: does_path_exist (a_old_path, Void)
			Name_is_valid: is_name_valid (a_new_path, Void)
			No_overwrite: not does_path_exist (a_new_path, Void)
		deferred
		ensure
			Path_exists: does_path_exist (a_new_path, Void)
			Moved: not does_path_exist (a_old_path, Void)
		end

	symlink_to (a_from_path, a_to_path: STRING) is
			-- Create file system symlink; This will create an file 
			-- system object with name `a_from_path' that is a symlink 
			-- to a real file `a_to_path'.
		require
			a_from_path /= Void and then not a_from_path.is_empty
			a_to_path /= Void and then not a_to_path.is_empty
			Name_is_valid: is_name_valid (a_to_path, Void)
								is_name_valid (a_from_path, Void)
			No_overwrite: not does_path_exist (a_from_path, Void)
		deferred
		ensure
			Path_exists: does_path_exist (a_from_path, Void)
		end

	unlink_to (a_from_path, a_to_path: STRING) is
			-- Remove file system symlink object; Assumes real change
			-- in parent path. 
		require
			a_from_path /= Void and then not a_from_path.is_empty
			a_to_path /= Void and then not a_to_path.is_empty
			Path_exists: does_path_exist (a_to_path, Void)
			Path_exists: does_path_exist (a_from_path, Void)
		deferred
		ensure
			Path_exists: does_path_exist (a_from_path, Void)
			Removed: not does_path_exist (a_to_path, Void)
		end

feature {ANY}
	-- Setting
	frozen build_namelist (an_fso: FILE_SYSTEM_OBJECT) is
			-- Capture names during creation
		do
			if not namelist.has (an_fso.name) then
				-- create sublist
				namelist.force (new_set, an_fso.name)
				check
					namelist.has (an_fso.name)
				end
			end
			namelist.item (an_fso.name).put_last (an_fso)
		ensure
			namelist.has (an_fso.name)
		end

feature {SYNCHRONISED_DIRECTORY}
	-- Features for logging
	log_rename_to (a_parent_path, a_name, an_new_name: STRING) is
			-- Log rename of a file system object `an_name' in directory
			-- `a_parent_path' to `an_new_name'.
		do
			if is_logging then
				log.rename_to (a_parent_path, a_name, an_new_name)
			end
		end

	log_move_to (a_old_path, a_new_path: STRING) is
			-- Log move of file system object to new path; Assumes real
			-- change in parent path.
		do
			if is_logging then
				log.move_to (a_old_path, a_new_path)
			end
		end

	log_symlink_to (a_old_path, a_new_path: STRING) is
			-- Log symlink of file system object to new path; Assumes real
			-- change in parent path.
		do
			if is_logging then
				log.symlink_to (a_old_path, a_new_path)
			end
		end

	log_unlink_to (a_old_path, a_new_path: STRING) is
			-- Log symlink of file system object to new path; Assumes real
			-- change in parent path.
		do
			if is_logging then
				log.unlink_to (a_old_path, a_new_path)
			end
		end

feature {SYNCHRONISED_DIRECTORY}
	-- Features for setting up object
	initialise (is_a_peer: BOOLEAN) is
			-- Get object to meet invariants. 
		do
			!DS_LINKED_LIST[SYNC_REPORT]!reports.make_default
			!DS_HASH_TABLE[DS_LIST[FILE_SYSTEM_OBJECT],STRING]!namelist.make_default
			is_peer := is_a_peer
		end

feature {SYNCHRONISED_DIRECTORY}
	-- Status report
	is_logging: BOOLEAN is
			-- Are we logging?
		do
			Result := log /= Void
		end

feature {SYNCHRONISED_DIRECTORY}
	-- Reporting status
	log_start (a_name: STRING) is
			-- Start logging
		require
			No_log: not is_logging
			Log_Name: a_name /= Void and then not a_name.is_empty
			Have_root: Current.root /= Void
		do
			!!log.make (a_name)
			-- TODO: Should query if logging is turned off and should get
			-- log name from configuration.
			if is_logging then
				log.directory (Current)
			end
		ensure
			Have_Log: is_logging
		end
	
	log_stop is
			-- Stop logging
		do
			if is_logging then
				log.close
				log := Void
			end
		end

feature {SYNCHRONISED_DIRECTORY}
	-- Access
	log: ACTION_LOG
			-- Logging object, no logging if void

feature {NONE}
	-- Manipulate the reports
	frozen do_report (a_report: SYNC_REPORT) is
			-- Perform all actions for this report (template method)
		require
			Valid_dir: not namelist.is_empty
			Valid_control: has_controller
			Valid_root: root /= Void and to_delete_base /= Void
		do
			if is_logging then
				log.report (a_report)
			end
			-- Notes: [? ?] <=> m_controller [user_wants_action
			-- user_is_done], letters (A), (B) indicate choices for user.
			m_controller.do_report (a_report)
		end

	m_controller: REPORT_CONTROL
			-- The control object for this directory
	
	frozen new_set: DS_LIST[FILE_SYSTEM_OBJECT] is
			-- Factory for namelist sub-list sets
		do
			!DS_LINKED_LIST[FILE_SYSTEM_OBJECT]!Result.make
		end

invariant
	Valid_namelist: namelist /= Void
	Valid_report_list: reports /= Void
	Either_peer_or_store: is_peer xor is_store
end -- class SYNCHRONISED_DIRECTORY
