indexing
	cluster: "demangler"
	namespace: "candidate"
	description: "The purpose of an instance of this class is to descend%
	% detect duplicated directory trees."
	original_author: "Justin Finnerty"
	original_date: "July 2005"
	current_date: "$Date: 2005/10/31 07:19:34 $"
	current_author: "$Author: finnerty $"
	revision: "$Revision: 2.6 $"	
	-- $Log: dirdup_report.ge,v $
	-- Revision 2.6  2005/10/31 07:19:34  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.5  2005/10/30 03:41:51  finnerty
	-- Split REPORT_VIEW into REPORT_CONTROL and REPORT_VIEW. Made changes to other classes as necessary
	--
	-- Revision 2.4  2005/09/30 06:49:12  finnerty
	-- Finalised for alpha release with SE and VE compilers
	--
	-- Revision 2.3  2005/09/28 13:40:22  finnerty
	-- Updated to work with ISE
	--
	-- Revision 2.2  2005/09/27 06:14:46  finnerty
	-- Updated to use GOBO and work with SE and VE
	--
	-- Revision 2.1  2005/09/06 22:56:16  finnerty
	-- Renamed *.e to *.ge for gepp
	--
class DIRDUP_REPORT
	-- The purpose of this class is to determine how closely two
	-- directory branches are copies.

inherit
	SYNC_REPORT
		redefine
			action,
			is_repeating_type,
			set_view
		end
#ifdef SE
insert
#endif
   CONFIGURATION_STATUS
   COMPARE_FILES

creation {ANY}
	-- Initialization
	make_dirdup_report

feature {ANY}
	-- Constants
	Red: BOOLEAN is True
			-- Indicate tree/object is red.
	Black: BOOLEAN is False
			-- Indicate tree/object is black.

feature {ANY}
	-- Access	
	black_root: FILE_SYSTEM_OBJECT
			-- The root of the current black tree.

	config_label: STRING is
			-- The label used for this report in the configuration file
		once
			Result := "duplicate-cutoff"
		end

	duplicate_cutoff: INTEGER
			-- Point at which to stop searching for duplicates

	help: UC_STRING is
			-- A description of what the report does.
		once
			!!Result.make_from_utf8 ("This report analyses a directory for duplicated branches. Once found the report attempts to merge or create symbolic links between the duplicated branches. When created, the symbolic links will be relative to the store directory selected.")
		end

	label: UC_STRING is
			-- A title or label for this report type
		once
			!!Result.make_from_utf8 ("Merge duplicated directories.")
		end
	
	merge_directories: BOOLEAN
			-- Should we merge (or create symlinks) the two directories

	merge_to_older: BOOLEAN
			-- Normally we merge from old to new. This will reverse this
			-- operation.

	red_root: FILE_SYSTEM_OBJECT
			-- The root of the current red tree.

feature {ANY}
	-- Status Report
	each_root_is_directory: BOOLEAN is
			-- Test if both roots are FSOs for directories.
		require
			Have_roots: have_roots
		do
			if red_root.properties.is_directory and then
				black_root.properties.is_directory then
				Result := True
			end
		end

	have_queues: BOOLEAN is
			-- Check that this report has its internal queues created.
		do
			Result := queue_merge_general /= Void and then queue_red_to_black_links /= Void and then queue_black_to_red_links /= Void
		end
	
	have_roots: BOOLEAN is
			-- Are the roots non-void?
		do
			Result := (red_root /= Void and black_root /= Void)
		ensure
			Result implies red_root /= Void
			Result implies black_root /= Void
		end

	is_renamed_pair: BOOLEAN
			-- Are the red/black pair different because one has been 
			-- renamed? (It may also have been moved)

	is_red_later: BOOLEAN is
			-- Does the red branch have later mod times than black?
		do
			-- Get sums
			Result := latest_mtime_red > latest_mtime_black
		end

   is_repeating_type: BOOLEAN is
		once
			Result := True
		end

	is_overlap: BOOLEAN is
			-- Do the mod times of the two trees overlap?
		do
			if not is_red_later then
				Result := latest_mtime_red < earliest_mtime_black
			else
				Result := latest_mtime_black < earliest_mtime_red
			end
		end

feature {ANY}
	-- Setting
	action is
			-- Perform all the actions in the transform queue
		require else
			Have_queues: have_queues
		local
			l_action_queue: DS_LIST[DP_COMMAND]
			l_parent: FILE_SYSTEM_OBJECT
		do
			if merge_directories then
				-- Already added general transforms and one clean-up 
				-- transform when we assumed `red_is_later' was true, now 
				-- and `merge_to_older' was false. We need to check these 
				-- assumptions hold.
				-- Need to fix if `red_is_later' = False
				if not is_red_later then
					-- In `generate' we assumed that red was later. Now
					-- we need to undo that assumption.
					transforms.last.remove
					transforms.remove_last
					-- And then add the correct transforms.
					queue_merge_general.wipe_out
					if is_renamed_pair then
						if red_root.has_parent and then
							red_root.parent.has_child_named (black_root.name) then
							push_backup (red_root.parent, red_root.parent.get_child_named (black_root.name))
						end
						push_rename(red_root, black_root.name)
					end
					l_parent := black_root.parent
					push_delete (black_root)
					push_move (l_parent, red_root)
					-- Should have all changes in list.
					do_pop_all
				end
			else
				-- All the transforms in the list are incorrect so we
				-- delete them.
				reset
				-- We symlink red to black (target) if `red_is_later' =
				-- False `merge_to_older' = False or `red_is_later' = True
				-- and `merge_to_older' = True
				if merge_to_older = is_red_later then
					l_action_queue := queue_red_to_black_links
					-- We need to swap red and black in `branch_top_set'.
					branch_top_set.remove_last
					branch_top_set.put_last (red_root)
				else
					l_action_queue := queue_black_to_red_links
				end
				-- Create the transforms by executing the commands in the
				-- queues.
				from
					l_action_queue.start
				until
					l_action_queue.off
				loop
					l_action_queue.item_for_iteration.execute
					l_action_queue.forth
				end
			end
			-- Now have all transforms in the transform list we call the 
			-- parent feature to `action' the transform list.
			Precursor
		end

	generate is
			-- Analyse a directory and create report by building up the
			-- `transforms' set.

			-- This method searches `sync_dir'.`namelist' for names
			-- with multiple FSOs.  A branch is detected iff: (a) FSOs
			-- with a common name have a set of parents with common
			-- names.  The last parent with a common name is the root.
			-- (b) FSOs have parents with different names but each parent
			-- has children with the same name.  The parent without a
			-- common name become the root.

			-- The feature starts by checking and resolving name pairs
			-- before checking names with more than two counterparts.
			-- There is a configurable `duplicate_cutoff' above which
			-- names with many FSO counterparts are ignored.  There is
			-- also a configurable `list_of_ignored_names' to ignore
			-- (which could contain "CVS", "makefile" etc..)
		local
			l_fso_list: DS_LIST[FILE_SYSTEM_OBJECT]
			l_fso_l, l_fso_u: INTEGER
			l_red, l_black: FILE_SYSTEM_OBJECT
		do
			if duplicate_cutoff < 2 then
				if config_manager.status = Continue and then
					config_manager.has (config_label) then
					if config_manager.item (config_label).value /= Void and then
						config_manager.item (config_label).value.is_integer then
						duplicate_cutoff := config_manager.item (config_label).value.to_integer
					elseif config_manager.item (config_label).default_value /= Void and then
						config_manager.item (config_label).default_value.is_integer  then
						duplicate_cutoff := config_manager.item (config_label).default_value.to_integer
					end
				end
			end
			if duplicate_cutoff >= 2 then
				if has_result then
					transforms.wipe_out
				end
				-- Find potential pairs for name lists of size two
				from
					sync_dir.namelist.start
				until
					sync_dir.namelist.off or else
					has_result
				loop
					l_fso_list := sync_dir.namelist.item_for_iteration
					if l_fso_list.count > 1 and
						l_fso_list.count <= duplicate_cutoff then					
						from
							l_fso_l := 1
						variant
							l_fso_list.count - l_fso_l + 1
						until
							l_fso_list.count <= l_fso_l or else
							has_result
						loop
							from
								l_fso_u := l_fso_l + 1
							variant
								l_fso_list.count - l_fso_u + 1
							until
								l_fso_list.count < l_fso_u or else
								has_result
							loop
								is_renamed_pair := False
								red_root := l_fso_list.item (l_fso_l)
								black_root := l_fso_list.item (l_fso_u)
								l_red := red_root
								l_black := black_root
								-- Attempt to walk to the top of the branch tree.
								branch_top_detect
								-- Check files and branch tops are unique, tops
								-- are not already in a branch_top_set and then 
								-- test if the files are identical.
								if l_red = l_black or else
									red_root = black_root or else
									branch_top_set.has (red_root) or else
									branch_top_set.has (black_root) or else
									not have_roots or else
									not compare_fso (l_red, l_black, Uncompressed, view) then
									reset_roots
								else
									debug
										view.report_warning ("** New branch ["+red_root.path+"]["+black_root.path+"] **")
									end
									-- Test roots
									if each_root_is_directory then
										-- roots must be directories to continue
										branch_merge
										branch_top_set.put_last (black_root)
										-- Should have all changes in list 
										-- except for moving one of the directories 
										-- into the trash.  Here we assume that 
										-- red is later and remove the 
										-- black root.
										push_delete (black_root)
										-- Now put all queued transforms into 
										-- the list.
										do_pop_all
									else
										reset_roots
									end
								end
								l_fso_u := l_fso_u + 1
							end
							l_fso_l := l_fso_l + 1
						end
					end
					sync_dir.namelist.forth
				end				
			end
		end

feature {DIRDUP_REPORT}
	-- Implementation
	branch_top_detect is
			-- This is a supplement to `generate' that moves
			-- to the top of commonly names FSOs then checks for
			-- condition (b) of `generate'
		require
			Have_roots: have_roots
		local
			r_indexer: FILE_SYSTEM_OBJECT
			counter: INTEGER
		do
			-- Walk up branch
			debug
				view.report_warning ("DEBUG: At start of branch_top_detect roots are ["+red_root.path+"]["+black_root.path+"]")
			end
			from
			until
				red_root = black_root or else
				red_root.parent = Void or else
				black_root.parent = Void or else
				not red_root.parent.name.is_equal (black_root.parent.name)
			loop
				red_root := red_root.parent
				black_root := black_root.parent
			end
			debug
				if red_root = black_root then
					view.report_warning ("DEBUG: Ascent exit [red_root = black_root]")
				end
				if red_root.parent = Void then
					view.report_warning ("DEBUG: Ascent exit [red_root.parent = Void]")
				end
				if black_root.parent = Void then
					view.report_warning ("DEBUG: Ascent exit [black_root.parent = Void]")
				end
				if red_root.parent /= Void and black_root.parent /= Void and then
					not red_root.parent.name.is_equal (black_root.parent.name) then
					view.report_warning ("DEBUG: Ascent exit [not red_root.parent.name.is_equal (black_root.parent.name)]")
				end
			end

			-- Check for renamed directory
			if red_root.parent /= Void and black_root.parent /= Void then
				from
					r_indexer := red_root.parent.first_child
					counter := 0
				until
					r_indexer = red_root.parent
				loop
					-- increment counter
					if black_root.parent.has_child_named (r_indexer.name) then
						counter := counter + 1
					end				
					-- step
					if r_indexer = red_root.parent.last_child then
						r_indexer := red_root.parent
					else
						r_indexer := r_indexer.next_sibling
					end
				end
				-- set renamed parent if more than 50% of parent directory
				-- contents are common.
				if 4 * counter > red_root.parent.count + black_root.parent.count then
					is_renamed_pair := True
					red_root := red_root.parent
					black_root := black_root.parent
				end
			end
			debug
				view.report_warning ("At end of branch_top_detect roots are ["+red_root.path+"]["+black_root.path+"]")
			end
		ensure
			Have_roots: have_roots
		end
	
	branch_merge is
			-- At this stage we have discovered branches that have at 
			-- least a file and its directory in common. We will get
			-- duplicates as a branch is likely to have many sub-nodes. 
			-- We eliminate duplicates by only inserting into the 
			-- a_branch_pairs list distinct branches.

			-- A more advanced implementation could avoid some of the
			-- need for duplicate checking by either unlinking one of the
			-- sub-branches and ask for user input later or get user input
			-- to resolve the sub-branch issue immediately.
		require
			Have_roots: have_roots
							each_root_is_directory
		local
			dir_stack: DS_STACK [DS_PAIR [FILE_SYSTEM_OBJECT, FILE_SYSTEM_OBJECT]]
			--a_branch_pairs: COLLECTION [TUPLE [FILE_SYSTEM_OBJECT, FILE_SYSTEM_OBJECT]]
			dir_tuple: DS_PAIR [FILE_SYSTEM_OBJECT, FILE_SYSTEM_OBJECT]
			red_fso, black_fso: FILE_SYSTEM_OBJECT
			black_counter: INTEGER
		do
			!DS_LINKED_STACK [DS_PAIR [FILE_SYSTEM_OBJECT, FILE_SYSTEM_OBJECT]]!dir_stack.make_default
			if queue_merge_general = Void then
				!DS_LINKED_LIST[DP_COMMAND]!queue_merge_general.make_default
			else
				queue_merge_general.wipe_out
			end
			if queue_red_to_black_links = Void then
				!DS_LINKED_LIST[DP_COMMAND]!queue_red_to_black_links.make_default
			else
				queue_red_to_black_links.wipe_out
			end
			if queue_black_to_red_links = Void then
				!DS_LINKED_LIST[DP_COMMAND]!queue_black_to_red_links.make_default
			else
				queue_black_to_red_links.wipe_out
			end
			-- Work down tree, directory by directory. Push any changes 
			-- into a record as we go.
			!!dir_tuple.make (red_root, black_root)
			from
				dir_stack.force (dir_tuple)
			until
				dir_stack.is_empty
			loop
				dir_tuple := dir_stack.item
				dir_stack.remove
				if dir_tuple.first.has_children then
					-- red directory not empty.
					from
						black_counter := 0
						red_fso := dir_tuple.first.first_child
					until
						red_fso = dir_tuple.first
					loop
						black_fso := Void
						if dir_tuple.second.has_children then
							black_fso := dir_tuple.second.get_child_named (red_fso.name)
						end
						if black_fso /= Void then
							-- Corresponding black FSO
							black_counter := black_counter + 1
							if red_fso.properties.is_directory then
								if black_fso.properties.is_directory then
									-- Both directories
									!!dir_tuple.make (red_fso, black_fso)
									dir_stack.force (dir_tuple)
								else
									-- Red is directory, black is file
									push_move_and_backup (red_fso.parent, black_fso)
								end
							else
								if black_fso.properties.is_directory then
									-- Black is directory, red is file
									push_backup (red_fso.parent, red_fso)
									push_move (red_fso.parent, black_fso)
								else
									-- Both files
									if compare_fso (red_fso, black_fso, Uncompressed, view) then
										push_symlink (red_fso, black_fso)
									else
										if red_fso.properties.time_modification >= black_fso.properties.time_modification then
											update_red_score (red_fso, black_fso)
											push_ignore (black_fso)
										else
											update_black_score (red_fso, black_fso)
											push_swap (red_fso, black_fso)
										end
									end
								end
							end
						else
							-- No corresponding black FSO:
							update_red_score (red_fso, Void)
						end
						if dir_tuple.first.has_children and then
							red_fso = dir_tuple.first.last_child then
							red_fso := dir_tuple.first
						else
							red_fso := red_fso.next_sibling
						end
					end
				end
				if black_counter < dir_tuple.second.count then
					-- Deal with remaining black children
					from
						black_fso := dir_tuple.second.first_child
					until
						black_fso = dir_tuple.second
					loop
						if not dir_tuple.first.has_child_named (black_fso.name) then
							push_move (dir_tuple.first, black_fso)
							update_black_score (Void, black_fso)
						end
						if dir_tuple.second.has_children and then
							black_fso = dir_tuple.second.last_child then
							black_fso := dir_tuple.second
						else
							black_fso := black_fso.next_sibling
						end
					end
				end
			end
		end

feature {REPORT_VIEW,SYNC_REPORT}
	-- Control the viewing object
	set_merge_directories (a_merge: BOOLEAN) is
			-- Will merge directories if `a_merge' is `True'
		do
			merge_directories := a_merge
		end

	set_merge_to_older (a_reverse_age: BOOLEAN) is
			-- Will "merge" directories to "older" directory if `a_merge'
			-- is `True'.
		do
			merge_to_older := a_reverse_age
		end

	set_view (a_controller: REPORT_CONTROL) is
			-- Set viewing object
		do
			view := a_controller.get_dirdup_viewer
			view.set_report (Current)
		end

feature {DIRDUP_REPORT}
	-- Access (Restricted)
	earliest_mtime_red: DT_DATE_TIME
			-- The `min' time in the red tree.

	earliest_mtime_black: DT_DATE_TIME
			-- The `min' time in the black tree.

	latest_mtime_red: DT_DATE_TIME
			-- The `max' time in the red tree.

	latest_mtime_black: DT_DATE_TIME
			-- The `max' time in the black tree.
	
feature {DIRDUP_REPORT}
	-- Transform queue manipulation
	do_pop_all is
			-- Perform all the actions in the transform queue
		require
			queue_merge_general /= Void
		do
			from
				queue_merge_general.start
			until
				queue_merge_general.off
			loop
				queue_merge_general.item_for_iteration.execute
				queue_merge_general.forth
			end
		end

	queue_merge_general: DS_LIST[DP_COMMAND]
			-- Queued transformations.
	
	queue_red_to_black_links: DS_LIST[DP_COMMAND]
			-- Set of transforms to "symlink" errors in `sync_dir'. May
			-- not be `Void'. This contains commands that will create
			-- links in the red directory to the black directory.
	
	queue_black_to_red_links: DS_LIST[DP_COMMAND]
			-- Set of transforms to "symlink" errors in `sync_dir'. May not
			-- be `Void'. This contains commands that will create
			-- links in the black directory to the red directory.
	
feature {DIRDUP_REPORT}
	-- Put transforms onto queue
	push_backup (a_parent, an_fso: FILE_SYSTEM_OBJECT) is
			-- Add a file backup
		local
#ifdef HAS_AGENT
			l_command: DELAYED_COMMAND_2[FILE_SYSTEM_OBJECT,FILE_SYSTEM_OBJECT]
#else
			l_command: RP_BACKUP_COMMAND
#endif			
		do
#ifdef HAS_AGENT
			!!l_command.make (agent new_backup_transform, a_parent, an_fso)
#else
			!!l_command.make (Current, a_parent, an_fso)
#endif			
			queue_merge_general.put_last (l_command)
		end
	
	push_delete (an_fso: FILE_SYSTEM_OBJECT) is
			-- Add a file backup
		local
#ifdef HAS_AGENT
			l_command: DELAYED_COMMAND[FILE_SYSTEM_OBJECT]
#else
			l_command: RP_DELETE_COMMAND
#endif			
		do
#ifdef HAS_AGENT
			!!l_command.make (agent new_delete_transform, an_fso)
#else
			!!l_command.make (Current, an_fso)
#endif			
			queue_merge_general.put_last (l_command)
		end
	
	push_ignore (an_fso: FILE_SYSTEM_OBJECT) is
			-- Do nothing
		do
		end
		
	push_move (a_parent, an_fso: FILE_SYSTEM_OBJECT) is
			-- Add a file move
		local
#ifdef HAS_AGENT
			l_command: DELAYED_COMMAND_2[FILE_SYSTEM_OBJECT,FILE_SYSTEM_OBJECT]
#else
			l_command: RP_MOVE_COMMAND
#endif			
		do
#ifdef HAS_AGENT
			!!l_command.make (agent new_move_transform, an_fso, a_parent)
#else
			!!l_command.make (Current, an_fso, a_parent)
#endif			
			queue_merge_general.put_last (l_command)
		end

	push_move_and_backup (a_parent, an_fso: FILE_SYSTEM_OBJECT) is
			-- Add a file backup and move
		local
#ifdef HAS_AGENT
			l_command: DELAYED_COMMAND_2[FILE_SYSTEM_OBJECT,FILE_SYSTEM_OBJECT]
#else
			l_command: DP_COMMAND
#endif			
		do
#ifdef HAS_AGENT
			!!l_command.make (agent new_backup_transform, a_parent, an_fso)
#else
			!RP_BACKUP_COMMAND!l_command.make (Current, a_parent, an_fso)
#endif			
			queue_merge_general.put_last (l_command)
#ifdef HAS_AGENT
			!!l_command.make (agent new_move_transform, an_fso, a_parent)
#else
			!RP_MOVE_COMMAND!l_command.make (Current, an_fso, a_parent)
#endif			
			queue_merge_general.put_last (l_command)
		end
	
	push_rename (an_fso: FILE_SYSTEM_OBJECT; a_name: STRING) is
			-- Add a file move
		local
#ifdef HAS_AGENT
			l_command: DELAYED_COMMAND_2[FILE_SYSTEM_OBJECT,STRING]
#else
			l_command: RP_RENAME_COMMAND
#endif			
		do
#ifdef HAS_AGENT
			!!l_command.make (agent new_rename_transform, an_fso, a_name)
#else
			!!l_command.make (Current, an_fso, a_name)
#endif			
			queue_merge_general.put_last (l_command)
		end

	push_swap (an_fso1, an_fso2: FILE_SYSTEM_OBJECT) is
			-- Exchange two FSOs
		local
			l_saved_name: STRING
			l_saved_parent: FILE_SYSTEM_OBJECT
		do
			l_saved_name := an_fso1.name
			l_saved_parent := an_fso2
			-- give red a unique name (relative to black parent)
			push_backup (l_saved_parent, an_fso1)
			-- move an_fso2 to red parent
			push_move (an_fso1.parent, an_fso2)
			-- move an_fso1 to black parent
			push_move (l_saved_parent, an_fso1)
			-- give red its name back
			push_rename (an_fso1, l_saved_name)
		end	

	push_symlink (a_red_fso, a_black_fso: FILE_SYSTEM_OBJECT) is
			-- Add a file link move
		local
#ifdef HAS_AGENT
			l_command: DELAYED_COMMAND_2[FILE_SYSTEM_OBJECT,STRING]
#else
			l_command: RP_SYMLINK_COMMAND
#endif			
		do
#ifdef HAS_AGENT
			!!l_command.make (agent new_symlink_transform, a_red_fso, a_black_fso.path)
#else
			!!l_command.make (Current, a_red_fso, a_black_fso.path)
#endif			
			queue_red_to_black_links.put_last (l_command)
#ifdef HAS_AGENT
			!!l_command.make (agent new_symlink_transform, a_black_fso, a_red_fso.path)
#else
			!!l_command.make (Current, a_black_fso, a_red_fso.path)
#endif			
			queue_black_to_red_links.put_last (l_command)
		end

feature {DIRDUP_REPORT}
	-- Update status
	reset_roots is
			-- Set both roots to Void
		do
			is_renamed_pair := False
			red_root := Void
			black_root := Void
		ensure
			Is_reset_roots: red_root = Void
								 black_root = Void
		end

	update_red_score (red_fso, black_fso: FILE_SYSTEM_OBJECT) is
			-- Store file modification times to later determine with
			-- branch is more recent. `black_fso' may be Void
		require
			red_fso /= Void
		do
			if latest_mtime_red < red_fso.properties.time_modification then
				latest_mtime_red := red_fso.properties.time_modification
			end
			if black_fso /= Void then
				if earliest_mtime_black > black_fso.properties.time_modification then
					earliest_mtime_black := black_fso.properties.time_modification
				end
			end
		end
	
	update_black_score (red_fso, black_fso: FILE_SYSTEM_OBJECT) is
			-- Store file modification times to later determine with
			-- branch is more recent. `red_fso' may be Void
		require
			black_fso /= Void
		do
			if latest_mtime_black < black_fso.properties.time_modification then
				latest_mtime_black := black_fso.properties.time_modification
			end
			if red_fso /= Void then
				if earliest_mtime_red > red_fso.properties.time_modification then
					earliest_mtime_red := red_fso.properties.time_modification
				end
			end
		end

feature {NONE}
	-- Initialization
	make_dirdup_report (a_sync_dir: SYNCHRONISED_DIRECTORY) is
			-- Initialise attributes to valid values
		do
			duplicate_cutoff := 0
			merge_directories := True
			merge_to_older := False
			!!earliest_mtime_red.make_from_epoch (0)
			!!earliest_mtime_black.make_from_epoch (0)
			!!latest_mtime_red.make_from_epoch (0)
			!!latest_mtime_black.make_from_epoch (0)
			!DS_LINKED_LIST [FILE_SYSTEM_OBJECT]!branch_top_set.make_default
			branch_top_set.put_last (a_sync_dir.to_delete_base)
			make_sync_report (a_sync_dir)
		end

feature {NONE}
	-- Access
	branch_top_set: DS_LINKED_LIST [FILE_SYSTEM_OBJECT]

end -- class DIRDUP_REPORT
