Click here to Skip to main content
15,896,118 members
Articles / Programming Languages / C++

C++ Template class to walk through a tree

Rate me:
Please Sign up or sign in to vote.
3.86/5 (7 votes)
1 Apr 20033 min read 66.7K   837   26  
An article decribing the use of templates in writing a tree walker
/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.
   Author: S.Nandagopal 16 March 2003
**************************************************************************/

//---------------------------------------------------------------------------
// Description: Defines CWalker template class.

#include <windows.h>
#include <string>
#include <iostream.h>

#import "msxml3.dll"


enum
{
	fail = 0,
	success = 1,
	stopped = 2
};

class CFolderContext
{
public:
	// Required types
	//
	typedef std::string ContextIdentifierType;
	typedef std::string ContentFilterType;

	CFolderContext()
	{}

	CFolderContext(const CFolderContext& o)
	{
		*this = o;
	}

	CFolderContext& operator = (const CFolderContext& o)
	{
	}
	
	// Required interface 
	//
	class CItem
	{
	public:
		// Required function
		//
		bool IsAContext() const
		{
			return fileInfo.dwFileAttributes==FILE_ATTRIBUTE_DIRECTORY;
		}

		WIN32_FIND_DATA& GetInfo()
		{
			return fileInfo;
		}

		const WIN32_FIND_DATA& GetInfo() const
		{
			return fileInfo;
		}
	private:
		WIN32_FIND_DATA fileInfo;
	};

	// Required interface
	// All the public methods are required
	//
	class COpenContext
	{
	public:
		COpenContext(const ContextIdentifierType& context,const ContentFilterType& filter)
		{
			m_context = context;
			std::string szToSearchFor = context;
			if(!filter.empty())
				 szToSearchFor += "\\" + filter;

			m_hFind = ::FindFirstFile(szToSearchFor.data(),&m_folderItem.GetInfo());
		}

		~COpenContext()
		{
			if(m_hFind!=INVALID_HANDLE_VALUE) ::FindClose(m_hFind);
		}

		const CItem* GetNext()
		{
			if(m_hFind==INVALID_HANDLE_VALUE) return 0;
			if(!::FindNextFile(m_hFind,&m_folderItem.GetInfo()))
				return 0;
			return &m_folderItem;
		}

		const CItem* Get() const
		{
			if(m_hFind==INVALID_HANDLE_VALUE) return 0;
			return &m_folderItem;	
		}

		bool GetNewContextId(ContextIdentifierType& newId) const
		{
			if(m_folderItem.IsAContext())
			{
				newId = m_context + "\\" + m_folderItem.GetInfo().cFileName;
				return true;
			}
			return false;
		}

	private:
		CItem m_folderItem;
		HANDLE m_hFind;
		ContextIdentifierType m_context;

		COpenContext(const COpenContext&);
		COpenContext& operator = (const COpenContext&);

	};

	// Required method
	//
	static bool IsReserved(const CItem& item)
	{
		if(std::string(".")==item.GetInfo().cFileName || std::string("..")==item.GetInfo().cFileName) return true;
		return false;
	}
};

//************************** CWalker template *************************************
// 
// Description: Takes two template arguments, UserDataTypeT, on which there are no
// restrictions, and ContextTypeT which, will have to comply to an interface.
//

template <class UserDataTypeT,class ContextTypeT>
class CWalker
{
public:
	typedef ContextTypeT ContextType;
	typedef UserDataTypeT UserDataType;
	typedef CWalker<UserDataType,ContextType> MyType;

	CWalker():m_bStopped(false)
	{}

	int Walk(UserDataType userData,
		const ContextType::ContextIdentifierType& iContext
			)
	{
		ContextType::ContentFilterType filter = m_filter;
		int nRet = WalkI(userData,iContext,filter,m_bStopped);
		m_bStopped = false;
		return nRet;
	}

	void SetContentFilter(const ContextType::ContentFilterType& filter)
	{
		m_filter = filter;
	}
	
	void StopWalking()
	{
		m_bStopped = true;
	}

private:

	int WalkI(UserDataType userData,
		const ContextType::ContextIdentifierType& iContext,
		const ContextType::ContentFilterType& filter,
		bool& bStopped
			)
	{
		if(bStopped) return stopped;

		ContextType::COpenContext openContext(iContext,filter);
		
		const ContextType::CItem *item = openContext.Get();

		if(!item) return fail;

		if(!item->IsAContext())
		{
			this->OnItemFound(userData,*item,iContext);
		}
		else 
		{
			if(!ContextType::IsReserved(*item))
			{
				ContextType::ContextIdentifierType newContext;
				openContext.GetNewContextId(newContext);

				if(this->OnContextChange(&userData,*item,newContext,true))
				{
					WalkI(userData,newContext,filter,bStopped);
					if(bStopped) return stopped;
					this->OnContextChange(&userData,*item,iContext,false);
				}
			}
			else this->OnItemFound(userData,*item,iContext);
		}

		while(true)
		{
			if(bStopped) return stopped;
			const ContextType::CItem *item = openContext.GetNext();
			if(!item)
				break;

			if(!item->IsAContext())
			{
				this->OnItemFound(userData,*item,iContext);
			}
			else 
			{
				if(!ContextType::IsReserved(*item))
				{
					ContextType::ContextIdentifierType newContext;
					openContext.GetNewContextId(newContext);

					if(this->OnContextChange(&userData,*item,newContext,true))
					{
						WalkI(userData,newContext,filter,bStopped);
						if(bStopped) return stopped;
						this->OnContextChange(&userData,*item,iContext,false);
					}
				}
				else this->OnItemFound(userData,*item,iContext);
			}
		}
		return success;
	}

	ContextType::ContentFilterType m_filter;

	virtual bool OnContextChange(UserDataType*,const ContextType::CItem&,const ContextType::ContextIdentifierType&,bool bMovedToNewOrBackToOld)
	{
		return false;
	}

	virtual void OnItemFound(UserDataType,const ContextType::CItem&,const ContextType::ContextIdentifierType&)
	{
	}

	bool m_bStopped;
};

class CMyFolderWalker:public CWalker<void*,CFolderContext>
{
public:
	CMyFolderWalker(bool bSurfaceWalk):m_bSurfaceWalk(bSurfaceWalk)
	{}
	void SetFileToFind(const std::string& szFileToFind)
	{
		m_szFileToBeFound = szFileToFind;
	}
private:

	bool OnContextChange(void**,const CFolderContext::CItem& item,const CFolderContext::ContextIdentifierType& szFolderPathAndName,bool bMovedToNewOrBackToOld)
	{
		if(bMovedToNewOrBackToOld)
		{
			cout << szTabs.data() << "****** folder " << szFolderPathAndName.data() << "******\n";
			szTabs += "\t";
			return !m_bSurfaceWalk;
		}
		else
		{
			szTabs.erase(szTabs.length()-1);
			cout << szTabs.data() << "************************************************\n";
		}
		return false;
	}

	void OnItemFound(void*,const CFolderContext::CItem& item,const CFolderContext::ContextIdentifierType& szPath)
	{
		std::string szTabsToUse = szTabs;
		if(!szTabsToUse.empty()) szTabsToUse.erase(szTabs.length()-1);

		cout << szTabsToUse.data() << "#### file #### " << item.GetInfo().cFileName << "\n";

		if(m_szFileToBeFound==item.GetInfo().cFileName)
		{
			cout << "$$$$$ File Found $$$$$\n";
			this->StopWalking();
		}
	}

	bool m_bSurfaceWalk;
	std::string szTabs;
	std::string m_szFileToBeFound;
};

int main(int argc, char* argv[])
{
	CMyFolderWalker folderWalker(false);
	folderWalker.SetContentFilter("*.*");
	folderWalker.SetFileToFind("gsdll32.dll");
	folderWalker.Walk(0,"D:\\gs");
	return 0;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Singapore Singapore
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions