indexing
	cluster: "demangler"
	namespace: "candidate"
	description: "Report errors in a `SYNCHRONISED_DIRECTORY' object."
	original_author: "Justin Finnerty"
	original_date: "July 2005"
	current_date: "$Date: 2005/10/30 03:41:51 $"
	current_author: "$Author: finnerty $"
	revision: "$Revision: 2.5 $"	
	-- $Log: duplicate_report.ge,v $
	-- 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/10/28 04:16:55  finnerty
	-- Added 'is_editable' for REPORT_VIEW upgrade.
	--
	-- Revision 2.3  2005/09/30 06:49:12  finnerty
	-- Finalised for alpha release with SE and VE compilers
	--
	-- Revision 2.2  2005/09/27 06:14:47  finnerty
	-- Updated to use GOBO and work with SE and VE
	--
	-- Revision 2.1  2005/09/06 22:55:28  finnerty
	-- Renamed *.e to *.ge for gepp
	--
	-- Revision 2.0  2005/08/26 23:31:39  finnerty
	-- == alpha version ==
	--
	-- Revision 1.1  2005/08/24 12:16:17  finnerty
	-- Base class for reports that look for duplicate file pairs.
	--
	-- Revision 1.1  2005/08/21 11:55:31  finnerty
	-- Alpha 1 version
	--
	-- Revision 1.2  2005/08/17 06:43:43  finnerty
	-- Added factory methods for TRANSFORM sub-types.
	--
	-- Revision 1.1  2005/08/16 15:05:32  finnerty
	-- Initial abstract data type for a report ona sync dir
	--

deferred class DUPLICATE_REPORT
	-- Report errors in a `SYNCHRONISED_DIRECTORY' object. This report
	-- is a base class for reports that search for duplicated files
	-- caused by backing up an original in some way (non-identical
	-- filenames).

inherit
	SYNC_REPORT
		redefine
			is_editable
		end
#ifdef SE
insert
#endif
   CONFIGURATION_STATUS
   COMPARE_FILES

feature {ANY}
	-- Access
	config_label: STRING is
			-- The label used for this report in the configuration file
		deferred
		end
	
	is_editable: BOOLEAN is
			-- Can the list of action in this report be edited?.
		do
			Result := True
		end

feature {ANY}
	-- Manipulate a report
	generate is
			-- Analyse a directory and create report by building up the
			-- `transforms' set.
		do
			if m_suffix_set.is_empty then
				update_suffix_set
			end
			-- (1) For each "backup" suffix, search for "backup"
			-- filenames.  (2) For each "backup" filename found search for
			-- "original" filename.  (3) If "backup" and "original" have
			-- the exact same size and  (4) have identical content on
			-- bitwise comparison  (5) add DELETE_TRANSFORM object for
			-- "backup" filename.
			from m_suffix_set.start until m_suffix_set.off loop
				do_suffix (m_suffix_set.item_for_iteration)
				m_suffix_set.forth
			end
		end

feature {DUPLICATE_REPORT}
	-- Creation methods
	make_duplicate_report (a_sync_dir: SYNCHRONISED_DIRECTORY) is
			-- Initialise attributes to valid values
		do
			!DS_LINKED_LIST[STRING]!m_suffix_set.make
			make_sync_report (a_sync_dir)
		end

feature {DUPLICATE_REPORT}
	-- Hidden methods
	frozen do_compare (a_list: DS_LIST[FILE_SYSTEM_OBJECT]; a_filename: STRING) is
			-- Compare a pair of files for identity. Should only be called
			-- from `do_match' or `do_suffix'.
		local
			l_list: DS_LIST[FILE_SYSTEM_OBJECT]
			l_fso1, l_fso2: FILE_SYSTEM_OBJECT
		do
			-- (3) If "backup" and "original" have the exact same size
			check
				-- This method should only be called from `do_match' so
				-- sync_dir will have entries for `a_filename' as-is and
				-- without `m_current_suffix'.
				sync_dir.namelist.has (a_filename)
#ifdef SE
				sync_dir.namelist.has (a_filename.substring (a_filename.lower, a_filename.upper - m_current_suffix.count))
#else
				sync_dir.namelist.has (a_filename.substring (1, a_filename.count - m_current_suffix.count))				
#endif
			end
#ifdef SE
			l_list := sync_dir.namelist.item (a_filename.substring (a_filename.lower, a_filename.upper - m_current_suffix.count))
#else
			l_list := sync_dir.namelist.item (a_filename.substring (1, a_filename.count - m_current_suffix.count))
#endif
			-- check parentage:
			from
				a_list.start
			until
				a_list.off
			loop
				l_fso1 := a_list.item_for_iteration
				from
					l_list.start
				until
					l_list.off
				loop
					l_fso2 := l_list.item_for_iteration
					if l_fso1.parent = l_fso2.parent then
						-- Are in the same directory.
						
						-- (4) have identical content on bitwise
						-- comparison
						if compare_fso (l_fso1, l_fso2, m_compress_type, view) then
							if m_compress_type = Uncompressed then
								new_delete_transform (l_fso1)
							else
								if l_fso1.properties.size > l_fso2.properties.size then
									new_delete_transform (l_fso1)
								else
									new_delete_transform (l_fso2)
								end
							end
						end
						
						-- Short-circuit: Don't need to do more comparisons
						l_list.go_after
					else
						l_list.forth
					end
				end
				a_list.forth
			end
		end
	
	frozen do_match (a_list: DS_LIST[FILE_SYSTEM_OBJECT]; a_filename: STRING) is
			-- Look for identical original file to this duplicate candidate
		do
			-- (2) For each candidate filename found search for
			-- "original" filename.
#ifdef SE
			if match_tail (a_filename) and then
				sync_dir.namelist.has (a_filename.substring (a_filename.lower, a_filename.upper - m_current_suffix.count)) then
#else
			if match_tail (a_filename) and then
				sync_dir.namelist.has (a_filename.substring (1, a_filename.count - m_current_suffix.count)) then
#endif
				-- a pair of files exist
				do_compare (a_list, a_filename)
			end
		end

	frozen do_suffix (a_suffix: STRING) is
			-- Search filename list for names with `m_current_suffix'
		do
			-- (1) For each duplicate suffix, search for pairs of files.
			set_current_suffix (a_suffix)
			from
				sync_dir.namelist.start
			until
				sync_dir.namelist.off
			loop
				do_match (sync_dir.namelist.item_for_iteration, sync_dir.namelist.key_for_iteration)
				sync_dir.namelist.forth
			end
		end

	frozen match_tail (a_name: STRING): BOOLEAN is
			-- Is `a_name' not equal to but ends in `m_current_suffix'?
		do
#ifdef SE
			Result := a_name.count > m_current_suffix.count and then
						 a_name.substring (a_name.upper - m_current_suffix.count + 1, a_name.upper).is_equal(m_current_suffix)
#else
			Result := a_name.count > m_current_suffix.count and then
						 a_name.substring (a_name.count - m_current_suffix.count + 1, a_name.count).is_equal(m_current_suffix)
#endif
		ensure
			Not_equal: Result implies not a_name.is_equal(m_current_suffix)
#ifdef SE
			Part_equal: Result implies a_name.substring (a_name.upper - m_current_suffix.count + 1, a_name.upper).is_equal(m_current_suffix)
#else
			Part_equal: Result implies a_name.substring (a_name.count - m_current_suffix.count + 1, a_name.count).is_equal(m_current_suffix)
#endif
		end

	frozen make_suffix_list_from_string (a_list: STRING) is
			-- Split a_list at every ':' and add to `m_suffix_set'.
		require
			a_list /= Void and then not a_list.is_empty
		local
			l_low, l_up: INTEGER
		do
			from
				l_low := 1
				l_up := 1
			until
				l_up = 0 or else
				l_up >= a_list.count
			loop
				l_up := a_list.index_of (':', l_low)
				if l_up /= 0 then
					if l_low /= l_up then
						m_suffix_set.put_last (a_list.substring (l_low, l_up - 1).string)
					end
					l_low := l_up + 1
					l_up := l_up + 1
				end
			end
			if l_low < a_list.count then
				m_suffix_set.put_last (a_list.substring (l_low, a_list.count).string)
			end
		end

	set_current_suffix (a_suffix: STRING) is
			-- change m_current_suffix
		do
			m_current_suffix := a_suffix
			m_compress_type := Uncompressed
		end

	frozen update_suffix_set is
			-- Update the list of suffixes from the configuration.
		do
			if config_manager.status = Continue and then
				config_manager.has (config_label) then
				if config_manager.item (config_label).value /= Void then
					make_suffix_list_from_string (config_manager.item (config_label).value)
				elseif config_manager.item (config_label).default_value /= Void then
					make_suffix_list_from_string (config_manager.item (config_label).default_value)
				end
			end
		end

feature {DUPLICATE_REPORT}
	-- Hidden attributes
	m_compress_type: INTEGER

	m_current_suffix: STRING
			-- Suffix we are currently searching for.
	
	m_suffix_set: DS_LIST[STRING]
			-- list of backup file name suffixes
invariant
	m_compress_type = Uncompressed or m_compress_type = Zip or m_compress_type = BZip2
end -- class DUPLICATE_REPORT
