indexing
	cluster: "file_system_object"
	namespace: "candidate"
	pattern: "abstract factory, template method, strategy"
	description: "Produce a virtual directory of FILE_SYSTEM_OBJECTs from a real file system directory."
	original_author: "Justin Finnerty"
	original_date: "July 2005"
	current_date: "$Date: 2005/09/28 13:38:04 $"
	current_author: "$Author: finnerty $"
	revision: "$Revision: 2.3 $"
	-- $Log: factory_for_fso.ge,v $
	-- Revision 2.3  2005/09/28 13:38:04  finnerty
	-- Updated to work with ISE
	--
	-- Revision 2.2  2005/09/06 22:54:29  finnerty
	-- Part upgrade to compile with GOBO library/tools and with SE/VE.
	--
	-- Revision 2.1  2005/09/05 02:34:07  finnerty
	-- Renamed *.e to *.ge for gepp
	--
	-- Revision 2.0  2005/08/26 23:30:21  finnerty
	-- == alpha version ==
	--
	-- Revision 1.7  2005/08/24 23:24:28  finnerty
	-- Replaced FILE_TOOLS with FSO_PROP_FACTORY for testing file existance.
	--
	-- Revision 1.6  2005/08/24 12:00:55  finnerty
	-- Possible bug in FILE_TOOLS caused OS max number of file handles to
	-- be reached during directory virtualisation (affecting report processing).
	-- As this was only used to avoid calling "fetch_properites", dropped
	-- FILE_TOOL object and paired  "fetch_properites" with "reset_properites"
	-- to avoid excessive memory usage.
	--
	-- Revision 1.5  2005/08/21 11:54:15  finnerty
	-- Alpha 1 version
	--
	-- Revision 1.4  2005/08/12 07:04:02  finnerty
	-- Rationalised ensure/invariant double up.
	--
	-- Revision 1.3  2005/08/10 02:21:37  finnerty
	-- Edited documentation
	--
	-- Revision 1.2  2005/08/09 13:45:35  finnerty
	-- Changes for alpha test.
	--
	-- Revision 1.1.1.1  2005/07/28 05:28:34  finnerty
	-- Imported eiffel sources
	--

	-- Combines concrete implementation of abstract factory pattern
	-- (`new_root', `new_child') with template method pattern
	-- (`virtualise_file_system'). Also uses strategy pattern to allow
	-- arbitrary computation on each new FILE_SYSTEM_OBJECT just after
	-- creation.

class FACTORY_FOR_FSO
	-- Produce a virtual directory of FILE_SYSTEM_OBJECTs from a real
	-- file system directory.

feature {ANY}
	-- queries
	frozen has_strategy: BOOLEAN is
			-- Do we have a strategy object?
		do
			Result := (strategy /= Void)
		end
		
	frozen virtualise_file_system (a_path: STRING): FILE_SYSTEM_OBJECT is
			-- Create a virtual representation of the file system. 
		require
			Valid_root_path: test_path (a_path)
		local
			l_stack: DS_LINKED_STACK[FILE_SYSTEM_OBJECT] -- `l_stack': stack used to eliminate recursion
			l_dir_cursor: KL_DIRECTORY -- `l_dir_cursor': object to access the filesystem
			l_parent, l_child: FILE_SYSTEM_OBJECT -- `l_parent',`l_child': references  to FSOs created by the method
		do
			-- Starting from `a_path', this method descends the directory
			-- tree generating a FSO object for every file system
			-- object. To reduce (eliminate?) the possibility of circular
			-- paths it does not descend symlinks to directories.

			-- The decision to not descend symlinks to directories was
			-- also taken because our main reason to get a representation
			-- of the file system was to perform advanced tree
			-- synchronisation operations. Our initial analysis showed
			-- that the synchronisation process should treat the symlinks
			-- themselves, not what they refer to, when comparing trees.
			
			-- Create the root object
			Result := new_root (a_path)
			if has_strategy then
				-- do strategy,
#ifdef HAS_AGENT
				strategy.call ([Result])
#else
				strategy.call (Result)
#endif
			end

			-- Create a stack with 100 entries
			create l_stack.make
			from
				-- and initialise with the root FSO.
				l_stack.force (Result)
			until
				-- Loop until the stack is empty.
				l_stack.is_empty
			loop
				-- Get and remove the top element of the stack
				l_parent := l_stack.item
				l_stack.remove
				-- Create the directory scanner
				!!l_dir_cursor.make (l_parent.path)
				-- Connect to the directory and,
				l_dir_cursor.open_read
				if l_dir_cursor.is_open_read then
					from
						-- read each child
						l_dir_cursor.read_entry
					until
						-- until the end.
						l_dir_cursor.end_of_input
					loop
						-- For a child that is not a current or parent directory
						if not (l_dir_cursor.last_entry.is_empty
								  or l_dir_cursor.last_entry.is_equal (".")
								  or l_dir_cursor.last_entry.is_equal ("..")) then
							-- create a new child object,
							l_child := new_child (l_parent, l_dir_cursor.last_entry)
							if has_strategy then
								-- do strategy
#ifdef HAS_AGENT
								strategy.call ([l_child])
#else
								strategy.call (l_child)
#endif
							end
							-- push on l_stack when it is a directory
							if l_child.exists and then
								l_child.properties.is_directory then
									l_stack.force (l_child)
							end
						end
						-- Get the next child. 
						l_dir_cursor.read_entry
					end
					l_dir_cursor.close
				end
			end
		end

#ifdef HAS_AGENT
	strategy: PROCEDURE[ANY,TUPLE[FILE_SYSTEM_OBJECT]]
#else
	strategy: AGENT_ARG[FILE_SYSTEM_OBJECT, BOOLEAN]
#endif
			-- Strategy object called on every new FSO. May be Void
	
feature {ANY}
	-- procedures
#ifdef HAS_AGENT
	frozen set_strategy (a_guest: PROCEDURE[ANY,TUPLE[FILE_SYSTEM_OBJECT]]) is
#else
	frozen set_strategy (a_guest: AGENT_ARG[FILE_SYSTEM_OBJECT, BOOLEAN]) is
#endif
			-- Set strategy routine
		require
			a_guest /= Void
		do
			strategy := a_guest
		ensure
			strategy = a_guest
		end

feature {FACTORY_FOR_FSO}
	-- private queries
	new_root (a_path: STRING): FILE_SYSTEM_OBJECT is
			-- Create root FILE_SYSTEM_OBJECT.
		require
			Valid_root_path: test_path (a_path)
		do
			!!Result.make_root (a_path)
		ensure
			Result /= Void
		end

	new_child (a_parent: FILE_SYSTEM_OBJECT; a_name: STRING): FILE_SYSTEM_OBJECT is
			-- Create child FILE_SYSTEM_OBJECT.
		require
			-- Valid_path: PATH (a_parent.path + a_name)
		do
			!!Result.make_child (a_name, a_parent)
		ensure
			Result /= Void
		end

feature {ANY}
	-- Implementation
	test_path (a_path: STRING): BOOLEAN is
			-- Check path existance
		local
			l_tester: FSO_PROP_FACTORY
		do
			!!l_tester.make_root (a_path)
			Result := l_tester.exists
		end
invariant
	has_strategy implies strategy /= Void

end -- class FACTORY_FOR_FSO
