Click here to Skip to main content
15,878,809 members
Articles / Mobile Apps

The StateWizard VC++ Add-in and Engine with Source Code

Rate me:
Please Sign up or sign in to vote.
4.73/5 (24 votes)
26 Mar 2009CPOL12 min read 190K   2.8K   132  
A cross-platform state-oriented application framework and a ClassWizard-like round-trip UML dynamic modeling/development tool that runs in popular IDEs. Aims at providing concurrent, distributed, and real-time application development tools for Win32/Linux
/**********************************************************************
UML StateWizard provides its software under the LGPL License and 
zlib/libpng License.

Email us at info@intelliwizard.com for any information, suggestions and 
feature requestions.

Home Page: http://www.intelliwizard.com
*************************************************************************/

// StateTreeStateTagDb.cpp : Implementation of CStateTreeCtrl
#include "stdafx.h"
#include "StateTreeCtrl.h"

/*
#include "AddAppDlg.h"
#include "AddEventDlg.h"
#include "CreateEventIdFileDlg.h"
#include "StateChartDlg.h"
#include "EventChange.h"
#include ".\statetreectrl.h"


#pragma comment(lib, "comctl32.lib")
*/

// CStateTreeCtrl
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


//////////////////////////////////////////////////////////////////////////
//  Create tag database operations
//
//
///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Create tag database
// INPUT: 
//        
// OUTPUT: 1) TRUE: 2)FALSE
// NOTE: 
//
///////////////////////////////////////////////////////////////////////////////////////////
BOOL CStateTreeCtrl::CreateTagDB()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	//Clear list to store the file path of event id list
	m_EventIdPathList.RemoveAll();

	TRACE(_T("CreateTagDB........\n"));

	// Get project list
	CStringArray strPrjNameList;
	strPrjNameList.RemoveAll();
	long lPrjCount;

	Fire_GetProjectList();
	data.Convert2StringArray(strPrjNameList, data.prjNameList);
	lPrjCount = (long)strPrjNameList.GetSize();

	if (lPrjCount == 0)
		return TRUE;
	m_tagDBIndex.RemoveAll();
	m_appSrcHdrFiles.RemoveAll();

	// Create tag database in order of project in project list respectively
	for (int nPrjSeqNum = 0; nPrjSeqNum < lPrjCount; nPrjSeqNum++)
	{
		// Sequence in m_appSrcHdrFiles should be consistent with sequence in m_tagDBIndex
		// hence, project name would only be stored in tag database rather in appSrcHdrFile
		// structure
		int nAppFileNum;

		nAppFileNum = (int)m_appSrcHdrFiles.GetSize();

		// Get handler of current project item in state tree and make this one selected
		CString strPrjName;

		strPrjName = strPrjNameList[nPrjSeqNum];

		/* Commented by Jerry.D . Project name is not the ful. file name.   
		strPrjName = strPrjName.Left(strPrjName.GetLength()-SUFFIXLEN);
		strPrjName.Replace('/', '\\');
		int idx = strPrjName.ReverseFind('\\'); 
		if(-1 != idx)
		strPrjName = strPrjName.Right(strPrjName.GetLength()-idx-1);
		*/
		TRACE(_T("strPrjName=%s\n"), strPrjName);

		HTREEITEM hWorkSpace = tree.GetRootItem();
		HTREEITEM hCurrentPrj = tree.GetChildItem(hWorkSpace);
		ASSERT(NULL != hCurrentPrj);

		while (true)
		{
			if (hCurrentPrj == NULL)
				break;

			CString sItemName = tree.GetItemText(hCurrentPrj);
			sItemName.Replace(" application(s)", NULL);
			sItemName.TrimLeft();
			sItemName.TrimRight();

			if (sItemName.CompareNoCase(strPrjName) == 0)
			{
				tree.SelectItem(hCurrentPrj); //found current project handler
				break;
			}
			hCurrentPrj = tree.GetNextSiblingItem(hCurrentPrj);
		}

		// Get selected project file list 
		CStringArray PrjFileList;
		PrjFileList.RemoveAll();

		//		BSTR bstr = strPrjNameList[nPrjSeqNum].AllocSysString();
		BSTR bstr = strPrjName.AllocSysString();
		Fire_GetProjectFileList(bstr);
		::SysFreeString(bstr);

		data.Convert2StringArray(PrjFileList, data.prjFileList);

		// Create a project index unit 
		TAG_DB_INDEX_T* pTagDBIndex = (TAG_DB_INDEX_T*)new(TAG_DB_INDEX_T);
		ASSERT(NULL != pTagDBIndex);
		pTagDBIndex->sPrjName = (char*)new char[strPrjName.GetLength()+1];
		ASSERT(NULL != pTagDBIndex->sPrjName);
		memcpy(pTagDBIndex->sPrjName, (LPCTSTR)strPrjName, strPrjName.GetLength()+1);
		// Get last write time 
		HANDLE hFile=CreateFile(strPrjNameList[nPrjSeqNum],
			GENERIC_READ,
			FILE_SHARE_READ,
			NULL,
			OPEN_EXISTING,
			FILE_FLAG_SEQUENTIAL_SCAN,
			0);
		SYSTEMTIME LastWriteSysTime = {0,0,0,0,0,0,0,0}; 
		if (hFile)
		{
			BY_HANDLE_FILE_INFORMATION FileInfo;
			GetFileInformationByHandle( hFile, &FileInfo );
			FILETIME LastWriteLocalTime = FileInfo.ftLastWriteTime;

			FileTimeToLocalFileTime( &(FileInfo.ftLastWriteTime), &LastWriteLocalTime );
			FileTimeToSystemTime( &LastWriteLocalTime, &LastWriteSysTime );	
			pTagDBIndex->LastWriteTime = LastWriteSysTime;
			CloseHandle( hFile );
		}
		else
			pTagDBIndex->LastWriteTime = LastWriteSysTime;
		pTagDBIndex->pDBFile = NULL;
		m_tagDBIndex.Add(pTagDBIndex);

		// Traverse each file in project and create tag database
		TAG_DB_FILE_INFO_T* pDBFileItem;

		//!!	LPCTSTR sOrignalStatus = GetStatusPaneText();  //************************************************//

		for(int i = 0; i < PrjFileList.GetSize(); i++)
		{
			CString sFilePath = PrjFileList[i];

			TRACE(_T("sFilePath=%s\n"), sFilePath);

			// Create a file index unit
			pDBFileItem = (TAG_DB_FILE_INFO_T*)new(TAG_DB_FILE_INFO_T);
			ASSERT(NULL != pDBFileItem);
			pDBFileItem->sFilePath = (char*)new char[sFilePath.GetLength()+1];
			memcpy(pDBFileItem->sFilePath, (LPCTSTR)sFilePath, sFilePath.GetLength()+1);
			ASSERT(NULL != pDBFileItem);
			pDBFileItem->pFuncList = NULL;
			pDBFileItem->pNextFile = NULL;
			pDBFileItem->pNextFile = ((TAG_DB_INDEX_T*)m_tagDBIndex[nPrjSeqNum])->pDBFile;
			((TAG_DB_INDEX_T*)m_tagDBIndex[nPrjSeqNum])->pDBFile = pDBFileItem;

			// Show the file name creating tag db.
			//!!		SetStatusPaneText(0, sFilePath); //************************************************//
			CStdioFile OpenedFile;		
			if (OpenedFile.Open(sFilePath, CFile::modeRead | CFile::typeText ) == 0)
			{
				// File exists in dsp file but does not exist in disk

				//DSUtils(m_pApp).PrintToOutputWindow(_T((CString)sFilePath+"		error: unexpected failure to open this file"));//************************************************//
				
				// _bstr_t bError1("Failure in create tag db: open a file");
				// Fire_OutPutStringToPane(bError1.GetBSTR());

				CString s = sFilePath+"		error: unexpected failure to open this file. This file may not exist.";
				_bstr_t bError2(s.GetBuffer());
				Fire_OutPutStringToPane(bError2.GetBSTR());
				s.ReleaseBuffer();
		

				delete(pDBFileItem->sFilePath);
				pDBFileItem->sFilePath = NULL;
				((TAG_DB_INDEX_T*)m_tagDBIndex[nPrjSeqNum])->pDBFile = (TAG_DB_FILE_INFO_T*)pDBFileItem->pNextFile;
				pDBFileItem->pNextFile = NULL;
				delete(pDBFileItem);
				continue;
			}

			UCHAR nCommentFlag = NO_COMMENTS_STATE; // flag used in marking state status in parsing code
			UCHAR nTagDBFlag = TAG_DB_CLASS_BEGIN_STATE; // flag used in marking state status in creating tag db
			UCHAR nReverseStateTreeFlag = TREE_GENERATION_BEGIN_STATE; // flag used in marking process status in generating state tree
			CString sCurrentLine = ""; // current line 
			CString sPrefixLine = "";  // record previous useful information for parsing function prototype
			CString sClassHead = "";
			CString sContexJoint = ""; // record '\' joint connections part
			CStringList ClassNameList;
			ClassNameList.RemoveAll();
			UINT nClassBraceNum[MAX_EMBEDDED_CLASS_DEPTH];
			UINT nFuncPos = 0; // record the { postion in function implementation
			UINT nFuncBraceNum = 0; // record the number of { and } in function implementation
			int nErrorPos;

			for (int j = 0; j < MAX_EMBEDDED_CLASS_DEPTH; j++)
				nClassBraceNum[j] = 0;

			CStringArray DefaultStateNameList; //record all default states in state tree
			std::vector<HTREEITEM> TreeItemList; //record all tree item needed to build the state tree		
			DefaultStateNameList.RemoveAll();
			TreeItemList.clear();

			while (true)
			{
				// Read a line from the file.
				if (!OpenedFile.ReadString(sCurrentLine))
					break;

				// Build state tree
				if ((nErrorPos = GenerateStateTree(FALSE, NULL, sCurrentLine, nPrjSeqNum, nReverseStateTreeFlag, TreeItemList, DefaultStateNameList, OpenedFile)) <= 0)
				{
					//					DSUtils(m_pApp).PrintToOutputWindow("Fail in building a state tree\n");//************************************************//
					_bstr_t bError1("Fail in building a state tree");
					Fire_OutPutStringToPane(bError1.GetBSTR());
					break;
				}


				// Filter comments part from current line of text
				BSTR bstr = sCurrentLine.AllocSysString();

				//Fire_ParseCommentState(bstr, nCommentFlag, NULL, &nFuncPos);

				CComVariant CountResultArray;
				CountResultArray.puintVal=NULL;
				CComVariant pFuncPos;
				pFuncPos.puintVal=&nFuncPos;

				Fire_ParseCommentState(bstr, nCommentFlag, CountResultArray,pFuncPos);
				nFuncPos=*(data.pFuncPosLine);
				sCurrentLine = (wchar_t*)data.sCleanLine;
				nCommentFlag = (UCHAR)data.CommentState;

				::SysFreeString(bstr);
				if (data.isParseCommentStateOk == TRUE)
				{
					// Whether mix code and comment.
					sContexJoint += sCurrentLine;
					continue;
				}

				// '\' joint symbol
				if (!sContexJoint.IsEmpty())
				{
					CString sMergedLine;

					sMergedLine = sContexJoint;				
					sMergedLine += sCurrentLine;
					sCurrentLine = sMergedLine;
					sContexJoint = "";
				}

				sCurrentLine.TrimLeft();
				sCurrentLine.TrimRight();
				if (!sCurrentLine.IsEmpty())// case 1: comments line; case 2: blank line
				{
					ParseToTagDB(sCurrentLine,sClassHead, &nTagDBFlag,&nFuncBraceNum,sPrefixLine,nFuncPos,&(pDBFileItem->pFuncList),ClassNameList, nClassBraceNum);
				}
			}

			if (nReverseStateTreeFlag == TREE_GENERATION_BEGIN_STATE)
			{
				// Non application files
				HTREEITEM hNonEventSrcFile = tree.GetChildItem(hCurrentPrj);
				ASSERT(NULL != hNonEventSrcFile);

				while (true)
				{
					if (((tree.GetItemData(hNonEventSrcFile)) & STATE_TREE_OTHER_SOURCE_MASK) != 0)
						break;

					hNonEventSrcFile = tree.GetNextSiblingItem(hNonEventSrcFile);
				}
				AddNonHdlerSrcToStateTree(FALSE, pDBFileItem, hNonEventSrcFile);

				// Sort tree node
				TV_SORTCB SortCB;
				SortCB.hParent = hNonEventSrcFile;
				SortCB.lpfnCompare = CompareFunc;
				SortCB.lParam = 0;

				tree.SortChildrenCB(&SortCB);
				SortCB.hParent = hCurrentPrj;
				SortCB.lpfnCompare = CompareFunc;
				SortCB.lParam = 0;
				tree.SortChildrenCB(&SortCB);

			}

			if (nReverseStateTreeFlag == TREE_GENERATION_END_STATE)
			{
				// Non event handlers in application files
				HTREEITEM hApp;

				hApp = m_NewApp;
				//				if (TreeItemList.size() != 0)
				//				{
				// In normal case, TreeItemList will not decrease to 0
				// but such condition is in case that exception happens
				// in parsing file.(Like: Build tree flag also exists in 
				// other source file which they are not meant to be tree 
				// flag ,instead, they are used by users by mistaken)
				//					hApp = *(TreeItemList.begin());
				//				}

				AddNonHdlerSrcToStateTree(FALSE, pDBFileItem, hApp);

				// Sort tree node
				TV_SORTCB SortCB;
				SortCB.hParent = hApp;
				SortCB.lpfnCompare = CompareFunc;
				SortCB.lParam = 0;
				tree.SortChildrenCB(&SortCB);

				SortCB.hParent = hCurrentPrj;
				SortCB.lpfnCompare = CompareFunc;
				SortCB.lParam = 0;
				tree.SortChildrenCB(&SortCB);

				tree.Expand(hCurrentPrj, TVE_EXPAND);

			}

			if (nReverseStateTreeFlag == TREE_GENERATION_STATE_TREE_DEFINE 
				|| nReverseStateTreeFlag == TREE_GENERATION_STATE_DECLARE 
				|| nReverseStateTreeFlag == TREE_GENERATION_EXCEPTION_STATE 
				|| nReverseStateTreeFlag == TREE_GENERATION_EXCEPTION_STATE_NO_TREE_HDR  
				|| nReverseStateTreeFlag == TREE_GENERATION_EXCEPTION_STATE_NO_STATE_DECL_HDR 
				|| nReverseStateTreeFlag == TREE_GENERATION_EXCEPTION_STATE_NO_APP_CLASS_DECL_HDR 
				|| nReverseStateTreeFlag == TREE_GENERATION_EXCEPTION_STATE_HANDLER_ERR
				)
			{
				// damaged application file 

				if (TreeItemList.size() != 0)
				{
					std::vector<HTREEITEM>::iterator pHItem = TreeItemList.begin();
					HTREEITEM hApp = (*pHItem);
					tree.DeleteItem(hApp);
				}

				if (nReverseStateTreeFlag != TREE_GENERATION_EXCEPTION_STATE_HANDLER_ERR)
				{
					CString sWarningMsg;
					if (nErrorPos < 0)
						nErrorPos *= (-1);
					else
						nErrorPos = nFuncPos+1;
					sWarningMsg.Format("%s(Line %d):	error: illegal syntax",OpenedFile.GetFileName(), nErrorPos);
					//!!			DSUtils(m_pApp).PrintToOutputWindow(sWarningMsg);
					//************************************************//
					_bstr_t bError(sWarningMsg.GetBuffer());
					Fire_OutPutStringToPane(bError.GetBSTR());
					sWarningMsg.ReleaseBuffer();
				}
			}
			DefaultStateNameList.RemoveAll();
			TreeItemList.clear();
			OpenedFile.Close(); 
		}

		// Reset the status bar to the original text.
		//!!		SetStatusPaneText(0,sOrignalStatus); //************************************************//
		if (nAppFileNum == m_appSrcHdrFiles.GetSize())
		{
			m_appSrcHdrFiles.Add(NULL); // meet a non application project
		}
	}
	GetEventIdListFilePath(ST_EVENT_HDR_FILE_UPDATE);
	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Clear tag database
// INPUT: 
//        
// OUTPUT: 1) TRUE: 2)FALSE
// NOTE: 
//
///////////////////////////////////////////////////////////////////////////////////////////
BOOL CStateTreeCtrl::ClearTagDB()
{
	// Clear tag database memory
	int i=0;
	for (i = 0; i < m_tagDBIndex.GetSize(); i++)
	{
		TAG_DB_INDEX_T* pDBIndexItem = (TAG_DB_INDEX_T*)m_tagDBIndex[i];
		TAG_DB_FILE_INFO_T* pDBFileItem = pDBIndexItem->pDBFile;

		while (true)
		{
			if (NULL == pDBFileItem)
				break;

			TAG_DB_FUNC_INFO_T* pDBFuncItem = pDBFileItem->pFuncList;
			TAG_DB_FILE_INFO_T* pDelFileItem = pDBFileItem;
			pDBFileItem = (TAG_DB_FILE_INFO_T*)pDBFileItem->pNextFile;
			while(true)
			{			
				if (NULL == pDBFuncItem)
					break;

				TAG_DB_FUNC_INFO_T* pDelItem = pDBFuncItem;
				pDBFuncItem = (TAG_DB_FUNC_INFO_T*)pDBFuncItem->pNextFunction;
				delete(pDelItem->key);
				delete(pDelItem->paramNameList);
				delete(pDelItem);
			}
			delete(pDelFileItem->sFilePath);
			delete(pDelFileItem);
		}
		delete(pDBIndexItem->sPrjName);
		delete(pDBIndexItem);
	}
	m_tagDBIndex.RemoveAll();

	// Clear application file information's memory
	for (i = 0; i < m_appSrcHdrFiles.GetSize(); i++)
	{
		APP_FILE_INFO_T* pAppFileItem = (APP_FILE_INFO_T*)m_appSrcHdrFiles[i];

		while (true)
		{
			if (NULL == pAppFileItem)
				break;
			APP_FILE_INFO_T* pDelAppItem = pAppFileItem;
			pAppFileItem = (APP_FILE_INFO_T*)pAppFileItem->pNextApp;

			delete(pDelAppItem->sAppName);
			if (pDelAppItem->sHdrFilePath != NULL)
				delete(pDelAppItem->sHdrFilePath);
			if (pDelAppItem->sSrcFilePath != NULL)
				delete(pDelAppItem->sSrcFilePath);
			delete(pDelAppItem);
		}
	}
	m_appSrcHdrFiles.RemoveAll();
	return TRUE;
}


/*******************************************************************************************
** DESCRIPTION: Traverse the project files and update all items related to the saved file
** INPUT: 1)sPrjName: Project name which contains clicking function 
**        2)sFilePath: File full path name.
** OUTPUT: 1) TRUE: Update Success 2)FALSE: Update Failed 3) -1: No other files can be updated 
** NOTE: When sFilePath is null, it means that we need to choose a random unsaved file to
**      save. This case happens when a new unsaved function need to be located.
**	sPrjName should not be empty.
**
*******************************************************************************************/
int CStateTreeCtrl::ExecuteUpdate(LPCTSTR sPrjName, CString sFilePath)
{
	// Get tag database corresponding item
	ASSERT(sPrjName!=NULL && sPrjName[0]!=0); // For debug edition.
	if (sPrjName==NULL || sPrjName[0]==0) return FALSE; // For release edition.

	TAG_DB_INDEX_T* pDBIndexItem=NULL;
	TAG_DB_FILE_INFO_T* pDBFileItem=NULL;

	for (int nPrjSeqNum = 0; nPrjSeqNum < m_tagDBIndex.GetSize(); nPrjSeqNum++)
	{
		pDBIndexItem = (TAG_DB_INDEX_T*)m_tagDBIndex[nPrjSeqNum];

		if (stricmp(sPrjName, pDBIndexItem->sPrjName) == 0)
			break;
	}
	if (pDBIndexItem) //Found
		pDBFileItem = pDBIndexItem->pDBFile;
	else 
		return -1; //Why???

	// Deal with null file path. It means the indicated function is a new one
	if (sFilePath == "")
	{
		// Get a random unsaved file
		BOOL bSaved = FALSE;
		while (true)
		{
			if (pDBFileItem == NULL)
				return -1;

			CString sPath = pDBFileItem->sFilePath;
			BSTR bstr = sPath.AllocSysString();
			Fire_TestOpenedDocIsModified(bstr, VARIANT_TRUE);
			::SysFreeString(bstr);
			if (data.isOpenedDocModified == TRUE)
				break;
			pDBFileItem = (TAG_DB_FILE_INFO_T*)pDBFileItem->pNextFile;
		}
		sFilePath = pDBFileItem->sFilePath;
		//.		sFilePath.MakeLower();
	}

	CString sFileName;
	sFileName = sFilePath;
	//.	sFileName.MakeLower();
	sFileName.Replace('/', '\\');
	int idx = sFileName.ReverseFind('\\'); 
	if(-1 != idx)
		sFileName = sFileName.Right(sFileName.GetLength()-idx-1);

	// Update modified parts in state tree 
	HTREEITEM hWorkSpace = tree.GetRootItem();
	HTREEITEM hCurrentPrj = tree.GetChildItem(hWorkSpace);
	HTREEITEM hItem;

	// Get handler of corresponding project tree item
	while (true)
	{
		if (hCurrentPrj == NULL)
			break;

		CString sItemName = tree.GetItemText(hCurrentPrj);
		sItemName.Replace(" application(s)", NULL);
		sItemName.TrimLeft();
		sItemName.TrimRight();

		if (!sItemName.CompareNoCase(sPrjName))
		{
			tree.SelectItem(hCurrentPrj);
			break;
		}
		hCurrentPrj = tree.GetNextSiblingItem(hCurrentPrj);
	}
	hItem = tree.GetChildItem(hCurrentPrj);

	BOOL bFound = FALSE;
	BOOL bResult = TRUE;  // record whether this time update is successful
	// Traverse each child tree item of indicated project item and update it if needed
	while (true)
	{
		if (hItem == NULL)
		{
			///////// no one is added
			if (bFound == FALSE)
			{
				BOOL bHandled = TRUE;
				OnRefresh(0,0,0,bHandled);
			}
			return bResult;
		}


		DWORD lMask = tree.GetItemData(hItem);
		if (lMask & STATE_TREE_APPLICATION_MASK)
		{
			HTREEITEM hSaved;

			hSaved = tree.GetChildItem(hItem);

			while (true)
			{
				if (NULL == hSaved)
					break;

				if (tree.GetItemData(hSaved) & (STATE_TREE_APPLICATION_SOURCE_FILE_MASK|STATE_TREE_APPLICATION_HEADER_FILE_MASK))
				{
					CString sFileNameToSearch = sFileName;
					CString sFileNameAtTree = tree.GetItemFileName(hSaved);
					if ((sFileNameToSearch).CompareNoCase(sFileNameAtTree) == 0)
					{
						bFound = TRUE;

						BOOL bReturn;
						bReturn = UpdateStateTree(hItem, hCurrentPrj, sFilePath);

						/* Duplicate with the actions in function UpdateStateTree();
						CString sTempFilePath = sFilePath;
						sTempFilePath.MakeLower();
						if (sTempFilePath.CompareNoCase(m_EventIdFilePath) == 0)
						{
						m_EventIdList.RemoveAll();
						CComVariant EventIdList_var;

						EventIdList_var.puintVal = reinterpret_cast<UINT*>(&m_EventIdList);
						Fire_GetEventIdList(CComBSTR(m_EventIdFilePath), EventIdList_var);
						data.Convert2StringArray(m_EventIdList, data.eventIdList);

						}
						*/
						if (bReturn == FALSE)
							bResult = bReturn;
						break;
					}
				}
				hSaved = tree.GetNextSiblingItem(hSaved);
			}
		} // if STATE_TREE_APPLICATION_MASK

		if (lMask & STATE_TREE_OTHER_SOURCE_MASK)
		{
			HTREEITEM hSaved;

			hSaved = tree.GetChildItem(hItem);

			while (true)
			{
				if (NULL == hSaved)
					break;
				TRACE("tree.GetItemText(hSaved)=%s\n", tree.GetItemText(hSaved));
				CString sTempFileName = sFileName;
				//sTempFileName.MakeLower();
				if (0==sTempFileName.CompareNoCase(tree.GetItemText(hSaved)))
				{
					bFound = TRUE;

					BOOL bReturn;
					bReturn = UpdateStateTree(hSaved, hCurrentPrj, sFilePath);

					/* Duplicate with the actions in function UpdateStateTree();
					CString sTempFilePath = sFilePath;
					sTempFilePath.MakeLower();
					if (sFilePath.CompareNoCase(m_EventIdFilePath) == 0)
					{
					m_EventIdList.RemoveAll();
					CComVariant EventIdList_var;

					EventIdList_var.puintVal = reinterpret_cast<UINT*>(&m_EventIdList);
					Fire_GetEventIdList(CComBSTR(m_EventIdFilePath), EventIdList_var);
					data.Convert2StringArray(m_EventIdList, data.eventIdList);

					}
					*/
					if (bReturn == FALSE)
						bResult = bReturn;
					return bResult;
				}
				hSaved = tree.GetNextSiblingItem(hSaved);
			}
		} // if STATE_TREE_OTHER_SOURCE_MASK
		hItem = tree.GetNextSiblingItem(hItem);
	} // while true
}

///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Parse code to get function implementations 
// INPUT:  1) sText: Code context with comments excluded to be parsed 
//         2) nTagDBFlag: Parser stage when processing parsing
//         3) sFuncHead: Record useful information from previous parsed lines
//		   4) nFuncPos: Line number of current parsed code's position 
//         5) nDocSeqNum: Sequence num in m_tagDBIndex
//         6) ClassNameList: Record parsed class name 
//         7) nClassBraceNum: Record braces which indicate validity of current class's field
//
// OUTPUT: 1) TRUE: 2)FALSE
// NOTE: Parse "class" or "struct" name first if thet exist, then parse	every function name in
//		 code. When each function has been parsed correctly, it will call CreateTagDBIndex to 
//		 record relative information into tag database.
// The grammar for function implementation in a class is 
// Syntax
// class-specifier :
// class-head { member-listopt }
// 
// class-head :
// class-key imodelopt identifieropt base-specopt
// class-key imodelopt class-nameopt base-specopt
// 
// class-key :
// class
// struct
// union
// 
// imodel :
// __declspec
///////////////////////////////////////////////////////////////////////////////////////////
BOOL CStateTreeCtrl::ParseToTagDB(CString sText, CString &sClassHead, UCHAR *nTagDBFlag, UINT *nFuncBraceNum, CString &sFuncHead, UINT nFuncPos, TAG_DB_FUNC_INFO_T** pDBFuncItem, CStringList &ClassNameList, UINT *nClassBraceNum)
{
	sText.TrimLeft();
	sText.TrimRight();

	CString sCurrentRecord = ""; // detract current line's useful information
	BOOL bJmpFromFuncParse = FALSE;// mark whether re-jumping to the loop is caused by function parsing's finish

	while(true)
	{
		// Case: initialized status or re-jump from function's parsing
		if (*nTagDBFlag == TAG_DB_CLASS_BEGIN_STATE)
		{
			sText.TrimLeft();
			sText.TrimRight();

			// Skip predefined macros
			if (sText.Find('#') == 0)                        
				break;
			// Skip empty line
			if (sText.IsEmpty())
				break;

			// Supported class keyword
			int nClassIdx = sText.Find("class");
			int nStructIdx = sText.Find("struct");
			int nUnionIdx = sText.Find("union");

			// Assume that class keyword must occur ahead of line
			if (nClassIdx == 0  || nStructIdx == 0 || nUnionIdx == 0)
			{
				// "class", "struct", "union" class-keys' length
				int nKeywordLen = (nStructIdx == 0)? 6: 5;   

				// Detract sequent text from class-keys
				CString sTmpText = sText.Right(sText.GetLength()-nKeywordLen);

				// Confirm class-keys' syntax to prevent from mistaking normal identifies which 
				// contain class-keys as a part for real class keys.
				if (sTmpText.IsEmpty() || sTmpText.GetAt(0) == ' ' || sTmpText.GetAt(0) == '\t' || sTmpText.GetAt(0) == '{') 
				{
					*nTagDBFlag = TAG_CLASS_KEYWORD_FOUND_STATE;

					sTmpText.TrimLeft();				
					sText = sTmpText;
					if (sText.IsEmpty())
						break;
					else
						continue;
				}
			}

			// Convert current state to TAG_CLASS_LEFT_BRACE_FOUND_STATE for detracting left braces 
			// which belongs to class-key's fields if class exists, otherwise, state will jump to 
			// TAG_DB_FUNCTION_BEGIN_STATE for parsing function implementation.
			if (ClassNameList.GetCount() == 0)
			{
				*nTagDBFlag = TAG_DB_FUNCTION_BEGIN_STATE;
			}
			else
			{
				// Count left braces of every existed class in valid field
				*nTagDBFlag = TAG_CLASS_LEFT_BRACE_FOUND_STATE;
			}
			continue;
		}

		// Case: class keyword has found and need get class name
		if (*nTagDBFlag == TAG_CLASS_KEYWORD_FOUND_STATE)
		{
			int nSemicolonIdx = sText.Find(';');
			int nLeftBraceIdx = sText.Find('{');

			// No '{' found or '{' occurs before ';''s occurrence, then confirm current
			// class-keys are not definitions.
			if ((nSemicolonIdx != -1 && nLeftBraceIdx != -1 && nSemicolonIdx < nLeftBraceIdx) ||
				nSemicolonIdx != -1 && nLeftBraceIdx == -1)
			{
				*nTagDBFlag = TAG_DB_CLASS_BEGIN_STATE;

				sText = sText.Right(sText.GetLength()-nSemicolonIdx-1);
				sText.TrimLeft();
				if (!sText.IsEmpty())
					continue;
				break;
			}
			else
			{
				// Convert to begin seeking class name 
				*nTagDBFlag = TAG_CLASS_NAME_BEGIN_STATE;

				// '{' occurs which means class name must exist among current text
				if (nLeftBraceIdx != -1)
				{
					*nTagDBFlag = TAG_CLASS_NAME_END_STATE;

					sClassHead += " ";
					sClassHead += sText.Left(nLeftBraceIdx);
					sText = sText.Right(sText.GetLength()-nLeftBraceIdx);
					continue;
				}

				// Temporarily store pseudo class name 
				sClassHead += sText;
				break;
			}
		}
		// Case: begin seeking class name
		if (*nTagDBFlag == TAG_CLASS_NAME_BEGIN_STATE)
		{
			// Class name confirmed until we found '{'
			int nLeftBraceIdx = sText.Find('{');

			if (nLeftBraceIdx == -1)
			{
				sClassHead += " ";
				sClassHead += sText;
				break;
			}
			else
			{
				// Once '{' occurs then directly jump to next state
				*nTagDBFlag = TAG_CLASS_NAME_END_STATE;

				sClassHead += " ";
				sClassHead += sText.Left(nLeftBraceIdx);
				sText = sText.Right(sText.GetLength()-nLeftBraceIdx);
				continue;
			}
		}
		// Case: parse class head to get the class name
		if (*nTagDBFlag == TAG_CLASS_NAME_END_STATE)
		{
			sClassHead.Replace('\t', ' '); 
			sClassHead.TrimLeft();
			sClassHead.TrimRight();

			// "class", "union", "struct" is anonymous structure
			if (sClassHead.IsEmpty())
			{
				*nTagDBFlag = TAG_CLASS_NAME_FOUND_STATE;

				ClassNameList.AddTail("");
				sClassHead = "";
				continue;
			}

			CString sTmpClassHead = sClassHead;
			// Deal with inheritance
			int nColonIdx = sClassHead.Find(':');
			if (nColonIdx != -1)
			{
				sTmpClassHead = sTmpClassHead.Left(nColonIdx);
				sTmpClassHead.TrimRight();
			}
			// Assume class limit word only can occur once at most ahead of class name
			int nSpaceIdx = sTmpClassHead.Find(' ');
			if (nSpaceIdx != -1)
			{
				sTmpClassHead = sTmpClassHead.Right(sTmpClassHead.GetLength()-nSpaceIdx-1);
				sTmpClassHead.TrimLeft();

				nSpaceIdx = sTmpClassHead.Find(' ');

				// Assume that a class head without inheritance statement and class limited word
				// which still have more than one word must not be a class declaration. It may be
				// a function that returns a class structure
				if (nSpaceIdx != -1)
				{
					*nTagDBFlag = TAG_DB_FUNCTION_BEGIN_STATE;

					// Recover the original line
					CString sMergedLine = sClassHead;
					sMergedLine += " ";
					sMergedLine += sText;
					sText = sMergedLine;

					sClassHead =""; // clear class head for next parsing
					continue;
				}
			}
			// Confirm a class structure
			*nTagDBFlag = TAG_CLASS_NAME_FOUND_STATE;

			ClassNameList.AddTail(sTmpClassHead);
			sClassHead = ""; // after retrieving class name, then clear class head
			continue;
		}
		// Case: class name is found to confirm whether a definition or a declaration
		if (*nTagDBFlag == TAG_CLASS_NAME_FOUND_STATE)
		{
			int nLeftBraceIdx = sText.Find('{');
			int nSemicolonIdx = sText.Find(';');

			// ';' occurs first, it is a class declaration 
			if ((nSemicolonIdx != -1 && nLeftBraceIdx != -1 && nSemicolonIdx < nLeftBraceIdx) ||
				nSemicolonIdx != -1 && nLeftBraceIdx == -1)
			{
				ClassNameList.RemoveTail();

				*nTagDBFlag = TAG_DB_CLASS_BEGIN_STATE;

				sText = sText.Right(sText.GetLength()-nSemicolonIdx-1);
				sText.TrimLeft();
				if (!sText.IsEmpty())
					continue;
				break;

			}
			// '{' becomes dominant. It is a class definition
			if (nLeftBraceIdx != -1)
			{
				*nTagDBFlag = TAG_CLASS_LEFT_BRACE_FOUND_STATE;

				// Embedded child class. Initialize state 
				if (bJmpFromFuncParse == TRUE)
				{
					bJmpFromFuncParse = FALSE;
					sFuncHead = "";
					sCurrentRecord = "";
				}
				nClassBraceNum[ClassNameList.GetCount()-1]++; // increase braces sum in corresponding class field

				// Retrieve remains to continue parsing 
				sText = sText.Right(sText.GetLength()-nLeftBraceIdx-1);
				sText.TrimLeft();
				sText.TrimRight();
				if (!sText.IsEmpty())
					continue;
			}
			break; // '{' also doesn't occur, then left it to next parsing 
		}
		// Case: count adjacent braces of the corresponding class. When meet '{', it will increase
		// sum of the braces by one. When meet '}', it will decrease by one. If the sum equals 0, then
		// we can confirm a class definition is over.
		if (*nTagDBFlag == TAG_CLASS_LEFT_BRACE_FOUND_STATE )
		{
			int rightBracesPos = sText.Find('}');
			int nLeftBraceIdx = sText.Find('{');
			if (nLeftBraceIdx == 0)                       //find a '{' which belonged to class field
			{
				int i = (int)ClassNameList.GetCount()-1;
				nClassBraceNum[i]++; // increase sum of braces 					 
				sText = sText.Right(sText.GetLength()-1);
				sText.TrimLeft();
				// Initialize state
				if (bJmpFromFuncParse == TRUE)
				{
					bJmpFromFuncParse = FALSE;
					sFuncHead = "";
					sCurrentRecord = "";
				}
				continue;
			}
			if (rightBracesPos == 0)					//find a '}' which belonged to class field
			{
				int i = (int)ClassNameList.GetCount()-1;
				nClassBraceNum[i]--;	// decrease sum of braces				
				sText = sText.Right(sText.GetLength()-1);
				sText.TrimLeft();
				if (nClassBraceNum[i] == 0)				//jump out of current class field
				{
					*nTagDBFlag = TAG_DB_CLASS_BEGIN_STATE;
					ClassNameList.RemoveTail();	
					continue;
				}
			}

			*nTagDBFlag = TAG_DB_FUNCTION_BEGIN_STATE;
			// No necessary to parse function part again cause it is jumped out of 
			// function parsing state
			if (bJmpFromFuncParse == TRUE)
				break;
			continue;
		}
		// Case: class name has been parsed if it exists. Then, it start to parse function part
		if (*nTagDBFlag == TAG_DB_FUNCTION_BEGIN_STATE)
		{
			// To seek '(' as a precursor for function implementation
			int lParenthesisPos = sText.Find("(");
			int nSemicolonIdx = sText.Find(";");

			// '(' occurs
			if(lParenthesisPos != -1)
			{
				*nTagDBFlag = TAG_FUNCTION_LEFT_PARENTHESIS_FOUND_STATE;	
				////////// prefix only used when ()pram list is separated with function name 
				if (sFuncHead.Find("::") == -1 && sText.Find("::") != 0 && lParenthesisPos != 0)
				{
					sFuncHead = "";
				}
				// Record probable function head part
				sCurrentRecord += sText.Left(lParenthesisPos+1);

				// Deal with remains of parsing part
				sText = sText.Right(sText.GetLength()-lParenthesisPos-1);
				sText.TrimLeft();
				sText.TrimRight();
				if (!sText.IsEmpty())
					continue;
				sFuncHead += " ";
				sFuncHead += sCurrentRecord;
				break;
			}
			// hasn't found ( but found ;
			else if (nSemicolonIdx != -1)
			{
				*nTagDBFlag = TAG_DB_CLASS_BEGIN_STATE;

				sText.TrimLeft();
				sText.TrimRight();
				// Exclude ';' out of parsing text for next round
				if (sText.GetAt(0) == ';')
					sText = sText.Right(sText.GetLength()-1);
				// Initialize accessory structure
				sFuncHead = "";
				sCurrentRecord = "";
				// No necessary to reparse the same part
				if (bJmpFromFuncParse == TRUE)
					break;
				bJmpFromFuncParse = TRUE;
				if (!sText.IsEmpty())
					continue;
				break;
			}
			else
			{
				sText.TrimLeft();
				// If it is not class related function, then no necessary to store last
				// stored function head part
				if (sText.Find("::") != 0 && sFuncHead.Find("::") == -1)
					sFuncHead = "";

				// Record current part to be probable function name or function's parent 
				// class name
				sCurrentRecord += sText;
				sFuncHead += " ";
				sFuncHead += sCurrentRecord;
				// Reparse the current part for probable embedded child class
				if (ClassNameList.GetCount() != 0)
				{
					*nTagDBFlag = TAG_DB_CLASS_BEGIN_STATE;
					bJmpFromFuncParse = TRUE; // mark it is jumping out of parsing function 
					continue;
				}
				break;
			}
		} // end of case
		// Case: '(' has got, then the aim is to get ')' 
		if (*nTagDBFlag == TAG_FUNCTION_LEFT_PARENTHESIS_FOUND_STATE)
		{
			// Find the position of ')'
			int rParenthesisPos = sText.Find(')');

			// ')' has found
			if (rParenthesisPos != -1)
			{
				// Store the part ahead of ')'
				*nTagDBFlag = TAG_FUNCTION_RIGHT_PARENTESIS_FOUND_STATE;

				sCurrentRecord += sText.Left(rParenthesisPos+1);				
				sText = sText.Right(sText.GetLength() - rParenthesisPos -1);
				sText.TrimLeft();
				sText.TrimRight();
				if (!sText.IsEmpty())
					continue;
				sFuncHead += " ";
				sFuncHead += sCurrentRecord;
				break;			
			}
			else
			{
				// ')' has not been found, then store current part. Since this part 
				// may become part of function's parameters
				sCurrentRecord += sText;
				sFuncHead += " ";
				sFuncHead += sCurrentRecord;
				break;
			}
		}// end of case
		// Case: ')' occurs and next step is to confirm whether it is an implementation or 
		// a declaration
		if (*nTagDBFlag == TAG_FUNCTION_RIGHT_PARENTESIS_FOUND_STATE)
		{
			// Get the position of '{' or ';'
			int nLeftBraceIdx = sText.Find('{');
			int nSemicolonIdx = sText.Find(";");

			// sub case 1: ";" occurs before "{"
			if ((nSemicolonIdx != -1 && nLeftBraceIdx != -1 && nSemicolonIdx < nLeftBraceIdx) ||
				nSemicolonIdx != -1 && nLeftBraceIdx == -1)
			{
				// Initialize state
				*nTagDBFlag = TAG_DB_CLASS_BEGIN_STATE;
				sFuncHead = "";
				sCurrentRecord = "";

				sText.TrimLeft();
				sText.TrimRight();
				if (sText.GetAt(0) == ';')
					sText = sText.Right(sText.GetLength()-1); // exclude ';' from part
				sText.TrimLeft();

				//		bJmpFromFuncParse = TRUE;
				if (!sText.IsEmpty())
					continue;
				break;
			}		
			// sub case 2: "{" occurs
			if (nLeftBraceIdx != -1)
			{
				*nTagDBFlag = TAG_DB_FUNCTION_END_STATE;	

				// Get "const" word for function if it exists 
				CString sTmpText = sText.Left(nLeftBraceIdx);
				sTmpText.TrimLeft();
				sTmpText.TrimRight();

				if(!sTmpText.Compare("const"))
					sCurrentRecord += "const";
				else if (!sTmpText.IsEmpty())
				{
					//need consider macro situation??????
					if (ClassNameList.GetCount() == 0  && 
						(sFuncHead.Find("::") == -1 || (sFuncHead.Find("::")!= -1 && sFuncHead.Find('(')!= -1&& sFuncHead.Find('(') < sFuncHead.Find("::"))) && 
						(sCurrentRecord.Find("::") == -1 || (sCurrentRecord.Find("::")!= -1 && sCurrentRecord.Find('(')!= -1 && sCurrentRecord.Find('(') < sCurrentRecord.Find("::"))))
					{
						*nTagDBFlag = TAG_DB_CLASS_BEGIN_STATE;
						sFuncHead = "";
						sCurrentRecord ="";
						continue;
					}
				}
				(*nFuncBraceNum)++; // count the "{" num
				sText = sText.Right(sText.GetLength()-nLeftBraceIdx-1);
				sText.TrimLeft();
				continue;
			}

			if (ClassNameList.GetCount() == 0  && 
				(sFuncHead.Find("::") == -1 || (sFuncHead.Find("::")!= -1 && sFuncHead.Find('(')!= -1&& sFuncHead.Find('(') < sFuncHead.Find("::"))) && 
				(sCurrentRecord.Find("::") == -1 || (sCurrentRecord.Find("::")!= -1 && sCurrentRecord.Find('(')!= -1 && sCurrentRecord.Find('(') < sCurrentRecord.Find("::"))))
			{
				*nTagDBFlag = TAG_DB_CLASS_BEGIN_STATE;
				sFuncHead = "";
				sCurrentRecord ="";
				continue;
			}
			sText.TrimLeft();
			sText.TrimRight();
			// Confirm whether it is function assignment 
			if (sText.GetAt(0) == ':')
			{
				// If it is function assignment, then convert state to skip it
				*nTagDBFlag = TAG_FUNCTION_COLON_ASSIGNMENT_STATE;
				sFuncHead += " ";
				sFuncHead += sCurrentRecord;
				continue;
			}
			// Get "const" word if it exists
			if (sText.Compare("const") == 0)
			{
				sCurrentRecord += "const";
				sFuncHead += " ";
				sFuncHead += sCurrentRecord;
				break;
			}
			*nTagDBFlag = TAG_DB_CLASS_BEGIN_STATE;
			sFuncHead = "";
			sCurrentRecord ="";
			continue;
		}
		// Case: function assignment process
		if (*nTagDBFlag == TAG_FUNCTION_COLON_ASSIGNMENT_STATE)
		{
			CString sTmpText;
			int nLeftBracePos = sText.Find('{');
			// '{' has not been found
			if (nLeftBracePos == -1)
			{
				sTmpText = sText;
				while (true)
				{
					int keywordPos = sTmpText.Find("const");
					if (keywordPos == -1)
						break;
					sTmpText = sTmpText.Right(sTmpText.GetLength()-keywordPos-5);
					sTmpText.TrimLeft();
					sTmpText.TrimRight();
					if (sTmpText.IsEmpty() && keywordPos != -1)
						sFuncHead += "const";
				}
				break;
			}
			else
			{
				*nTagDBFlag = TAG_FUNCTION_RIGHT_PARENTESIS_FOUND_STATE;
				sTmpText = sText.Left(nLeftBracePos);
				while (true)
				{
					int keywordPos = sTmpText.Find("const");
					if (keywordPos == -1)
						break;
					sTmpText = sTmpText.Right(sTmpText.GetLength()-keywordPos-5);
					sTmpText.TrimLeft();
					sTmpText.TrimRight();
					if (sTmpText.IsEmpty() && keywordPos != -1)
						sFuncHead += "const";
				}
				sText = sText.Right(sText.GetLength()-nLeftBracePos);
				sText.TrimLeft();
				if (!sText.IsEmpty())
					continue;
				break;
			}
		}
		// Case: { has found and pass through the current function implementation
		if (*nTagDBFlag == TAG_DB_FUNCTION_END_STATE)
		{
			//for the first time in this case it will be handled at once
			sCurrentRecord = sFuncHead+sCurrentRecord;
			sCurrentRecord.TrimLeft();
			sCurrentRecord.TrimRight();

			if (!sCurrentRecord.IsEmpty())
			{
				//// Create DB index
				CString sClassName = "";

				// Get class name if it exists
				POSITION pos;
				pos = ClassNameList.GetHeadPosition();
				for (;pos!=NULL;)
				{
					sClassName += ClassNameList.GetAt(pos);
					sClassName += "::"; //  support anonymous class name
					ClassNameList.GetNext(pos);
				}

				CString key;
				CString paramNameList;
				// Convert to the stored function prototype in the form of tag database
				GetTagDBKeyFromFunc(sCurrentRecord, key, paramNameList, sClassName);
				// Create tag database index
				CreateTagDBIndex(pDBFuncItem, nFuncPos, key, paramNameList);

				sFuncHead = "";
				sCurrentRecord = "";
			}
			CheckFuncBraces(sText, nFuncBraceNum);
			//// has reached the end of function implementation
			if (*nFuncBraceNum == 0)
			{
				*nTagDBFlag = TAG_DB_CLASS_BEGIN_STATE;

				sText.TrimLeft();
				sText.TrimRight();
				if (sText.GetAt(0) == ';')
					sText = sText.Right(sText.GetLength()-1);
				sText.TrimLeft();
				sText.TrimRight();
				if (!sText.IsEmpty())
					continue;
			}
			break;
		}// end of case
	}// end of while
	if (*nTagDBFlag == TAG_DB_FUNCTION_BEGIN_STATE)
		*nTagDBFlag = TAG_DB_CLASS_BEGIN_STATE;
	return TRUE;
}




///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Create index in tag database
// INPUT: 1) pDBFuncItem: pointer to the recorded function information structure
//        2) nFuncPos: line number of function implementation 
//        3) sKeyName: stored function prototype in tag database
//        4) sParamNameList: stored function parameters of stored function
//		  
// OUTPUT: TRUE: index created successfully
//		   FALSE: 
///////////////////////////////////////////////////////////////////////////////////////////
BOOL CStateTreeCtrl::CreateTagDBIndex(TAG_DB_FUNC_INFO_T** pDBFuncItem, UINT nFuncPos, CString sKeyName, CString sParamNameList)
{
	TAG_DB_FUNC_INFO_T* pNewRecord = (TAG_DB_FUNC_INFO_T*)new(TAG_DB_FUNC_INFO_T);
	ASSERT(NULL != pNewRecord);
	pNewRecord->key = (char*)new char[sKeyName.GetLength()+1];
	ASSERT(NULL != pNewRecord->key);
	pNewRecord->paramNameList = (char*)new char[sParamNameList.GetLength()+1];
	ASSERT(NULL != pNewRecord->paramNameList);	
	memcpy(pNewRecord->key, (LPCTSTR)sKeyName, sKeyName.GetLength()+1);
	memcpy(pNewRecord->paramNameList, (LPCTSTR)sParamNameList, sParamNameList.GetLength()+1);
	pNewRecord->nPosLine = nFuncPos;
	pNewRecord->pNextFunction = *pDBFuncItem;
	*pDBFuncItem = pNewRecord;
	return TRUE;
}

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Alex "Question is more important than the answer."

Comments and Discussions