mergetodolist.cpp

来自「管理项目进度工具的原代码」· C++ 代码 · 共 336 行

CPP
336
字号
// MergeToDoList.cpp: implementation of the CMergeToDoList class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "MergeToDoList.h"
#include "tdlschemadef.h"
#include "Taskfile.h"

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

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CMergeToDoList::CMergeToDoList(TDL_MERGEBY nBy, TDL_MERGEHOW nHow) 
	: m_pXIDestRoot(NULL), m_nMergeBy(nBy), m_nMergeHow(nHow), m_dwNextID(0)
{
}

CMergeToDoList::~CMergeToDoList()
{

}

int CMergeToDoList::Merge(LPCTSTR szSrcPath, LPCTSTR szDestPath)
{
	CTaskFile fileSrc(TDL_ROOT);

	if (fileSrc.Load(szSrcPath))
		return Merge(fileSrc.Root(), szDestPath);

	return 0;
}

int CMergeToDoList::Merge(const CXmlItem* pXISrc, CXmlItem* pXIDest)
{
	// all the external methods end up here
	m_pXIDestRoot = pXIDest;
	m_dwNextID = pXIDest->GetItemValueI(TDL_NEXTUNIQUEID);

	int nRes = MergeTasks(pXISrc, pXIDest);

	m_pXIDestRoot = NULL;

	return nRes;
}

int CMergeToDoList::Merge(const CXmlItem* pXISrc, LPCTSTR szDestPath)
{
	CTaskFile fileDest(TDL_ROOT);

	if (fileDest.Load(szDestPath))
		return Merge(pXISrc, fileDest.Root());

	return 0;
}

int CMergeToDoList::Merge(LPCTSTR szSrcPath, CXmlItem* pXIDest)
{
	CTaskFile fileSrc(TDL_ROOT);

	if (fileSrc.Load(szSrcPath))
		return Merge(fileSrc.Root(), pXIDest);

	return 0;
}

int CMergeToDoList::MergeTasks(const CXmlItem* pXISrc, CXmlItem* pXIDest)
{
	// simple checks first
	if (!pXISrc || !pXIDest)
		return 0;

	ASSERT (!(m_nMergeBy == TDLM_BYTITLE && m_dwNextID == 0));

	if (m_nMergeBy == TDLM_BYTITLE && m_dwNextID == 0)
		return 0;

	BuildLookupMap();

	switch (m_nMergeBy)
	{
	case TDLM_BYID:
		return MergeTasksByID(pXISrc, pXIDest);

	case TDLM_BYTITLE:
		return MergeTasksByTitle(pXISrc, pXIDest);
	}

	// else
	return 0;
}

int CMergeToDoList::MergeTasksByID(const CXmlItem* pXISrc, CXmlItem* pXIDest)
{
	// task ID must be same for both
	DWORD dwSrcID = pXISrc->GetItemValueI(TDL_TASKID);
	DWORD dwDestID = pXIDest->GetItemValueI(TDL_TASKID);
	
	if (dwSrcID != dwDestID)
		return 0;
	
	// if the item is a task (could be the root) merge task attributes
	int nMerged = 0;
	
	if (dwSrcID)
	{
		// keep track of max ID
		m_dwNextID = max(m_dwNextID, dwSrcID + 1);

		if (MergeAttributes(pXISrc, pXIDest))			
			nMerged++;
	}
	
	// now merge child items individually.
	const CXmlItem* pXISrcChild = pXISrc->GetItem(TDL_TASK);
	
	if (pXISrcChild)
		nMerged += MergeTaskByID(pXISrcChild, pXIDest);

	// then merge next sibling similarly (if we are a task)
	if (dwSrcID)
	{
		const CXmlItem* pXISrcNext = pXISrc->GetSibling();

		if (pXISrcNext)
			nMerged += MergeTaskByID(pXISrcNext, pXIDest->GetParent());
	}
	
	return nMerged;
}

int CMergeToDoList::MergeTaskByID(const CXmlItem* pXISrc, CXmlItem* pXIDestParent)
{
	ASSERT(pXISrc);
	ASSERT(pXIDestParent);

	DWORD dwID = pXISrc->GetItemValueI(TDL_TASKID);
	ASSERT(dwID);

	int nMerged = 0;
	
	// look up this item in the dest by ID
	CXmlItem* pXIDest = FindDestTask(dwID);
	
	// if no such item exists then simply add child to dest
	if (!pXIDest)
	{
		// create placeholder
		pXIDest = pXIDestParent->AddItem(TDL_TASK);
		
		// set the id
		pXIDest->AddItem(TDL_TASKID, (int)dwID);
	}
	else
	{
		// else if the dest items parent is not pXIDest and the user wants to
		// move existing items then first move the item before merging
		CXmlItem* pXICurDestParent = pXIDest->GetParent();

		if (pXICurDestParent != pXIDestParent && m_nMergeHow == TDLM_MOVE)
		{
			if (pXICurDestParent->RemoveItem(pXIDest))
				pXIDest = pXIDestParent->AddItem(pXIDest);
		}
	}
	
	// then merge attributes
	nMerged = MergeTasksByID(pXISrc, pXIDest);

	return nMerged;
}

int CMergeToDoList::MergeTasksByTitle(const CXmlItem* pXISrc, CXmlItem* pXIDest)
{
	// merge our attributes first
	int nMerged = 0;
	
	// if both these items are root items (have no parents) then we just
	// merge their children else if the task name does not match then we don't merge
	if (pXISrc->GetParent())
	{
		ASSERT (pXISrc->NameMatches(TDL_TASK));
		ASSERT (pXISrc->NameMatches(pXIDest));
		ASSERT (pXIDest->GetParent());
		ASSERT (pXISrc->ItemValueMatches(pXIDest, TDL_TASKTITLE));

		if (!pXISrc->ItemValueMatches(pXIDest, TDL_TASKTITLE))
			return 0;
	}
	else
		ASSERT (!pXIDest->GetParent());

	if (MergeAttributes(pXISrc, pXIDest))
		nMerged++;

	// merge children
	// essentially we simply add whatever tasks exist in pXISrc 
	// which do not exist in pXIDest and merge duplicates
	const CXmlItem* pXISrcChild = pXISrc->GetItem(TDL_TASK);

	while (pXISrcChild)
	{
		// cycle pXIDest children looking for a match
		CXmlItem* pXIDestChild = pXIDest->GetItem(TDL_TASK);

		while (pXIDestChild)
		{
			if (pXIDestChild->ItemValueMatches(pXISrcChild, TDL_TASKTITLE))
				break;

			// next child
			pXIDestChild = pXIDestChild->GetSibling();
		}
			
		// if no match found then create new placeholder
		if (!pXIDestChild)
		{
			TRACE ("CToDoCtrl::Merge(adding %s to %s)\n", 
					pXISrcChild->GetItemValue(TDL_TASKTITLE), 
					pXIDest->GetItemValue(TDL_TASKTITLE));

			pXIDestChild = pXIDest->AddItem(TDL_TASK);
			pXIDestChild->AddItem(TDL_TASKTITLE, pXISrcChild->GetItemValue(TDL_TASKTITLE));
			pXIDestChild->AddItem(TDL_TASKID, (int)m_dwNextID++);
			
			nMerged++;
		}

		// finally merge the child items
		nMerged += MergeTasksByTitle(pXISrcChild, pXIDestChild);

		// then do next source child
		pXISrcChild = pXISrcChild->GetSibling();
	}

	return nMerged;
}

BOOL CMergeToDoList::MergeAttributes(const CXmlItem* pXISrc, CXmlItem* pXIDest)
{
	// don't merge if the source is older than the dest
	const CXmlItem* pXISrcMod = pXISrc->GetItem(TDL_TASKLASTMOD);
	const CXmlItem* pXIDestMod = pXIDest->GetItem(TDL_TASKLASTMOD);

	if (pXISrcMod && pXIDestMod)
	{
		if (pXISrcMod->GetValueF() <= pXIDestMod->GetValueF())
			return FALSE;
	}
	else if (pXIDestMod)
		return FALSE;

	BOOL bChange = FALSE;
	POSITION pos = pXISrc->GetFirstItemPos();

	while (pos)
	{
		const CXmlItem* pXISrcAttrib = pXISrc->GetNextItem(pos);

		// don't merge task ID or subtasks
		if (pXISrcAttrib->NameMatches(TDL_TASK) || pXISrcAttrib->NameMatches(TDL_TASKID))
			continue;
		
		// don't merge last mod unless last mod does not exist in dest task
		if (pXISrcAttrib->NameMatches(TDL_TASKLASTMOD) && pXIDestMod)
			continue;

		// does it already exist?
		LPCTSTR szAttrib = pXISrcAttrib->GetName();
		CXmlItem* pXIDestAttrib = pXIDest->GetItem(szAttrib);

		// if not then add it
		if (!pXIDestAttrib)
		{
			pXIDest->AddItem(*pXISrcAttrib, FALSE);
			bChange = TRUE;
		}
		// else update it
		else
		{
			// update value
			if (!pXIDestAttrib->ValueMatches(pXISrcAttrib, FALSE))
			{
				pXIDestAttrib->SetValue(pXISrcAttrib->GetValue());
				bChange = TRUE;
			}

			// and any children
			if (pXISrcAttrib->GetItemCount())
				bChange |= MergeAttributes(pXISrcAttrib, pXIDestAttrib);
		}
	}

	return bChange;
}

CXmlItem* CMergeToDoList::FindDestTask(DWORD dwID)
{
	if (!dwID)
		return NULL;

	CXmlItem* pXITask = NULL;
	m_mapID2Item.Lookup(dwID, pXITask);

	return pXITask;
}

void CMergeToDoList::BuildLookupMap()
{
	m_mapID2Item.RemoveAll();
	AddTaskToLookupMap(m_pXIDestRoot);
}

void CMergeToDoList::AddTaskToLookupMap(CXmlItem* pXIDest)
{
	if (!pXIDest)
		return;

	DWORD dwID = pXIDest->GetItemValueI(TDL_TASKID);

	if (dwID)
		m_mapID2Item[dwID] = pXIDest;

	// first child
	AddTaskToLookupMap(pXIDest->GetItem(TDL_TASK));

	// next sibling
	AddTaskToLookupMap(pXIDest->GetSibling());
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?