//----------------------------------------------------------------------------- // Microsoft OLE DB RowsetViewer // Copyright (C) 1994 - 2000 By Microsoft Corporation. // // @doc // // @module LIST.H // //----------------------------------------------------------------------------------- #ifndef _LIST_H_ #define _LIST_H_ ///////////////////////////////////////////////////////////////////////////// // Includes // ///////////////////////////////////////////////////////////////////////////// #include ///ULONG_MAX ///////////////////////////////////////////////////////////////////////////// // Defines // ///////////////////////////////////////////////////////////////////////////// typedef void* POSITION; ///////////////////////////////////////////////////////////////////////////// // CNode // ///////////////////////////////////////////////////////////////////////////// template class CNode { public: // constructors CNode(CNode* pPrevNode, CNode* pNextNode); virtual ~CNode(); // members TYPE m_data; // element data CNode* m_pNext; // next CNode CNode* m_pPrev; // prev CNode }; ///////////////////////////////////////////////////////////////////////////// // CNode::CNode // ///////////////////////////////////////////////////////////////////////////// template CNode::CNode(CNode* pPrevNode, CNode* pNextNode) { //Constructor m_pPrev = pPrevNode; m_pNext = pNextNode; //NOTE: The constructor doesn't have an element passed in for //data. This is so we don't have a copy of the parameter and then //need to copy it for assignment... } ///////////////////////////////////////////////////////////////////////////// // CNode::~CNode // ///////////////////////////////////////////////////////////////////////////// template CNode::~CNode() { //Destructor m_pPrev = NULL; m_pNext = NULL; } ///////////////////////////////////////////////////////////////////////////// // CList // ///////////////////////////////////////////////////////////////////////////// template class CList { public: //constructors CList(); virtual ~CList(); //members private: //data CNode* m_pNodeHead; // Head of CList CNode* m_pNodeTail; // Tail of CList ULONG m_ulElements; // Elements in the list public: //list modifying operations virtual POSITION AddHead(ARG_TYPE element); // Add to Head virtual POSITION AddTail(ARG_TYPE element); // Add to Tail virtual POSITION InsertBefore(POSITION position, ARG_TYPE element); // Add before position virtual POSITION InsertAfter(POSITION position, ARG_TYPE element); // Add after position virtual TYPE RemoveHead(); // Remove from Head virtual TYPE RemoveTail(); // Remove from Tail virtual TYPE RemoveAt(POSITION position); // RemoveAt position void RemoveAll(); // Remove all elements //Peek methods inline POSITION GetHeadPosition() const { return m_pNodeHead; } inline POSITION GetTailPosition() const { return m_pNodeTail; } inline TYPE GetHead() const { ASSERT(m_pNodeHead); return m_pNodeHead->m_data; } inline TYPE& GetHead() { ASSERT(m_pNodeHead); return m_pNodeHead->m_data; } inline TYPE GetTail() const { ASSERT(m_pNodeTail); return m_pNodeTail->m_data; } inline TYPE& GetTail() { ASSERT(m_pNodeTail); return m_pNodeTail->m_data; } virtual TYPE GetNext(POSITION& position) const; // Next element virtual TYPE& GetNext(POSITION& position); // Next element virtual TYPE GetPrev(POSITION& position) const; // Prev element virtual TYPE& GetPrev(POSITION& position); // Prev element //Data methods virtual TYPE GetAt(POSITION position) const; //Get element value virtual TYPE& GetAt(POSITION position); //Get element value virtual void SetAt(POSITION position, ARG_TYPE element); //Set element value //Array-like methods virtual POSITION FindIndex(ULONG iIndex) const; //Index element virtual POSITION Find(ARG_TYPE element, POSITION startAfter = NULL) const; //informational methods inline BOOL IsEmpty() const { return m_ulElements==0; } inline ULONG GetCount() const { return m_ulElements; } }; ///////////////////////////////////////////////////////////////////////////// // CList::CList // ///////////////////////////////////////////////////////////////////////////// template CList::CList() { //constructor m_pNodeHead = NULL; m_pNodeTail = NULL; m_ulElements = 0; } ///////////////////////////////////////////////////////////////////////////// // CList::~CList // ///////////////////////////////////////////////////////////////////////////// template CList::~CList() { //Remove all elements RemoveAll(); } ///////////////////////////////////////////////////////////////////////////// // CList::AddHead // ///////////////////////////////////////////////////////////////////////////// template POSITION CList::AddHead(ARG_TYPE element) { //Add to the Head of the CList, (stack) CNode* pNewNode = new CNode(NULL, m_pNodeHead); if(pNewNode) { pNewNode->m_data = element; //If there was a list hook the head->prev to the new head if(m_pNodeHead) m_pNodeHead->m_pPrev = pNewNode; else m_pNodeTail = pNewNode; m_pNodeHead = pNewNode; m_ulElements++; } return pNewNode; } ///////////////////////////////////////////////////////////////////////////// // CList::AddTail // ///////////////////////////////////////////////////////////////////////////// template POSITION CList::AddTail(ARG_TYPE element) { //Add to the m_pNodeTail of the CList CNode* pNewNode = new CNode(m_pNodeTail, NULL); if(pNewNode) { pNewNode->m_data = element; if(m_pNodeTail != NULL) m_pNodeTail->m_pNext = pNewNode; else m_pNodeHead = pNewNode; m_pNodeTail = pNewNode; m_ulElements++; } return pNewNode; } ///////////////////////////////////////////////////////////////////////////// // CList::GetNext // ///////////////////////////////////////////////////////////////////////////// template TYPE CList::GetNext(POSITION& position) const { ASSERT(position); //Set position to the next element CNode* pNode = (CNode*)position; position = pNode->m_pNext; //return the current element return pNode->m_data; } ///////////////////////////////////////////////////////////////////////////// // CList::GetNext // ///////////////////////////////////////////////////////////////////////////// template TYPE& CList::GetNext(POSITION& position) { ASSERT(position); //Set position to the next element CNode* pNode = (CNode*)position; position = pNode->m_pNext; //return the current element return pNode->m_data; } ///////////////////////////////////////////////////////////////////////////// // CList::GetPrev // ///////////////////////////////////////////////////////////////////////////// template TYPE CList::GetPrev(POSITION& position) const { ASSERT(position); //Set position to the next element CNode* pNode = (CNode*)position; position = pNode->m_pPrev; //return the current element return pNode->m_data; } ///////////////////////////////////////////////////////////////////////////// // CList::GetPrev // ///////////////////////////////////////////////////////////////////////////// template TYPE& CList::GetPrev(POSITION& position) { ASSERT(position); //Set position to the next element CNode* pNode = (CNode*)position; position = pNode->m_pPrev; //return the current element return pNode->m_data; } ///////////////////////////////////////////////////////////////////////////// // CList::GetAt // ///////////////////////////////////////////////////////////////////////////// template TYPE CList::GetAt(POSITION position) const { ASSERT(position); CNode* pNode = (CNode*)position; return pNode->m_data; } ///////////////////////////////////////////////////////////////////////////// // CList::GetAt // ///////////////////////////////////////////////////////////////////////////// template TYPE& CList::GetAt(POSITION position) { ASSERT(position); CNode* pNode = (CNode*)position; return pNode->m_data; } ///////////////////////////////////////////////////////////////////////////// // CList::SetAt // ///////////////////////////////////////////////////////////////////////////// template void CList::SetAt(POSITION position, ARG_TYPE element) { ASSERT(position); //Save the old data CNode* pNode = (CNode*)position; //Store new data pNode->m_data = element; } ///////////////////////////////////////////////////////////////////////////// // CList::RemoveHead // ///////////////////////////////////////////////////////////////////////////// template TYPE CList::RemoveHead() { //Remove and return from the Head of the List ASSERT(m_pNodeHead); CNode* pOldHead = m_pNodeHead; // pointer to the Removed node TYPE element = GetHead(); //make a copy, before deleteing m_pNodeHead = pOldHead->m_pNext; // reroute Head to exclude the first element if(m_pNodeHead) { ASSERT(m_pNodeTail); m_pNodeHead->m_pPrev = NULL; } else { m_pNodeTail = NULL; } m_ulElements--; delete pOldHead; // delete head return element; } ///////////////////////////////////////////////////////////////////////////// // CList::RemoveTail // ///////////////////////////////////////////////////////////////////////////// template TYPE CList::RemoveTail() { //Remove and return from the m_pNodeTail of the CList ASSERT(m_pNodeTail); CNode* pOldTail = m_pNodeTail; TYPE element = GetTail(); //make a copy before deleteing m_pNodeTail = pOldTail->m_pPrev; if(m_pNodeTail) { ASSERT(m_pNodeHead); m_pNodeTail->m_pNext = NULL; } else { m_pNodeHead = NULL; } m_ulElements--; delete pOldTail; return element; } ///////////////////////////////////////////////////////////////////////////// // CList::RemoveAt // ///////////////////////////////////////////////////////////////////////////// template TYPE CList::RemoveAt(POSITION position) { //Remove CList[position] ASSERT(position); CNode* pOldNode = (CNode*)position; TYPE element = GetAt(position); //make a copy before deleteing // If removing the head if(pOldNode == m_pNodeHead) { m_pNodeHead = pOldNode->m_pNext; } else { pOldNode->m_pPrev->m_pNext = pOldNode->m_pNext; } if (pOldNode == m_pNodeTail) { m_pNodeTail = pOldNode->m_pPrev; } else { pOldNode->m_pNext->m_pPrev = pOldNode->m_pPrev; } m_ulElements--; delete pOldNode; return element; } ///////////////////////////////////////////////////////////////////////////// // CList::RemoveAll // ///////////////////////////////////////////////////////////////////////////// template void CList::RemoveAll() { // Remove all items from the CList CNode* pNode = m_pNodeHead; while(pNode) { CNode* pTemp = pNode; pNode = pNode->m_pNext; delete pTemp; } m_pNodeHead = NULL; m_pNodeTail = NULL; m_ulElements = 0; } ///////////////////////////////////////////////////////////////////////////// // CList::InsertBefore // ///////////////////////////////////////////////////////////////////////////// template POSITION CList::InsertBefore(POSITION position, ARG_TYPE element) { //insert before the position if(position == m_pNodeHead) // Add before Head return AddHead(element); CNode* pOldNode = (CNode*)position; //otherwise a little more difficult CNode* pNewNode = new CNode(pOldNode->m_pPrev, pOldNode); if(pNewNode) { pNewNode->m_data = element; //Hook up before after nodes to it if(pOldNode->m_pPrev) { pOldNode->m_pPrev->m_pNext = pNewNode; } else { m_pNodeHead = pNewNode; } m_ulElements++; } return pNewNode; } ///////////////////////////////////////////////////////////////////////////// // CList::InsertAfter // ///////////////////////////////////////////////////////////////////////////// template POSITION CList::InsertAfter(POSITION position, ARG_TYPE element) { //insert after the position if(position == m_pNodeTail) // Add after the m_pNodeTail return AddTail(element); CNode* pOldNode = (CNode*)position; //other wise a little more difficult CNode* pNewNode = new CNode(pOldNode, pOldNode->m_pNext); if(pNewNode) { pNewNode->m_data = element; //Hook up before after nodes to it if(pOldNode->m_pNext) { pOldNode->m_pNext->m_pPrev = pNewNode; } else { m_pNodeTail = pNewNode; } m_ulElements++; } return pNewNode; } ///////////////////////////////////////////////////////////////////////////// // CList::FindIndex // ///////////////////////////////////////////////////////////////////////////// template POSITION CList::FindIndex(ULONG iIndex) const { ASSERT(iIndex>=0 && iIndex* pNode = m_pNodeHead; //Find the specified index while(iIndex--) pNode = pNode->m_pNext; return (POSITION)pNode; } ///////////////////////////////////////////////////////////////////////////// // CList::Find // ///////////////////////////////////////////////////////////////////////////// template POSITION CList::Find(ARG_TYPE element, POSITION pPosAfter) const { CNode* pNode = m_pNodeHead; if(pPosAfter) pNode = ((CNode*)pPosAfter)->m_pNext; //return pointer to found element for(CNode* p = pNode; p != NULL; p = p->m_pNext) if(p->m_data == element) return p; // return position to found CNode return NULL; // return NULL if not found } ///////////////////////////////////////////////////////////////////////////// // CAssoc // ///////////////////////////////////////////////////////////////////////////// template class CAssoc { public: // constructors CAssoc(); virtual ~CAssoc(); // members KEY m_key; // key VALUE m_value; // element data }; ///////////////////////////////////////////////////////////////////////////// // CAssoc::CAssoc // ///////////////////////////////////////////////////////////////////////////// template CAssoc::CAssoc() { //Constructor // m_key = key; // m_value = value; // element data //NOTE: The constructor doesn't have an element passed in for //data. This is so we don't have a copy of the parameter and then //need to copy it for assignment... } ///////////////////////////////////////////////////////////////////////////// // CAssoc::~CAssoc // ///////////////////////////////////////////////////////////////////////////// template CAssoc::~CAssoc() { } ///////////////////////////////////////////////////////////////////////////// // CMap // ///////////////////////////////////////////////////////////////////////////// template class CMap { public: // Construction CMap(); virtual ~CMap(); // number of elements int GetCount(); BOOL IsEmpty(); // Lookup BOOL Lookup(ARG_KEY key, VALUE& rValue); // add a new (key, value) pair BOOL SetAt(ARG_KEY key, ARG_VALUE newValue); // removing existing (key, ?) pair BOOL RemoveKey(ARG_KEY key); void RemoveAll(); // iterating all (key, value) pairs POSITION GetStartPosition(); void GetNextAssoc(POSITION& rNextPosition, KEY& rKey, VALUE& rValue); // Implementation protected: POSITION GetPosition(ARG_KEY key); CAssoc* AssocFromPos(POSITION); CList*, CAssoc*> m_listValues; }; ///////////////////////////////////////////////////////////////////////////// // CMap // ///////////////////////////////////////////////////////////////////////////// template CMap::CMap() { } ///////////////////////////////////////////////////////////////////////////// // ~CMap // ///////////////////////////////////////////////////////////////////////////// template CMap::~CMap() { m_listValues.RemoveAll(); } ///////////////////////////////////////////////////////////////////////////// // CMap::GetPosition // ///////////////////////////////////////////////////////////////////////////// template POSITION CMap::GetPosition(ARG_KEY key) { //Find this "key" in the list... //NOTE: This is an extremly slow process for a CMap class. //This is a linear search to just make the code simple and quick, //This really should be a hashtable lookup POSITION pos = m_listValues.GetHeadPosition(); while(pos) { if(AssocFromPos(pos)->m_key == key) return pos; m_listValues.GetNext(pos); } return NULL; } ///////////////////////////////////////////////////////////////////////////// // CMap::AssocFromPos // ///////////////////////////////////////////////////////////////////////////// template CAssoc* CMap::AssocFromPos(POSITION pos) { if(!pos) return NULL; CNode*>* pNode = (CNode*>*)pos; return pNode->m_data; } ///////////////////////////////////////////////////////////////////////////// // CMap::GetStartPosition // ///////////////////////////////////////////////////////////////////////////// template POSITION CMap::GetStartPosition() { return m_listValues.GetHeadPosition(); } ///////////////////////////////////////////////////////////////////////////// // CMap::GetCount // ///////////////////////////////////////////////////////////////////////////// template int CMap::GetCount() { return m_listValues.GetCount(); } ///////////////////////////////////////////////////////////////////////////// // CMap::IsEmpty // ///////////////////////////////////////////////////////////////////////////// template BOOL CMap::IsEmpty() { return m_listValues.IsEmpty(); } ///////////////////////////////////////////////////////////////////////////// // CMap::Lookup // ///////////////////////////////////////////////////////////////////////////// template BOOL CMap::Lookup(ARG_KEY key, VALUE& rValue) { POSITION pos = GetPosition(key); if(!pos) return FALSE; rValue = AssocFromPos(pos)->m_value; return TRUE; } ///////////////////////////////////////////////////////////////////////////// // CMap::RemoveAll // ///////////////////////////////////////////////////////////////////////////// template void CMap::RemoveAll() { m_listValues.RemoveAll(); } ///////////////////////////////////////////////////////////////////////////// // CMap::RemoveKey // ///////////////////////////////////////////////////////////////////////////// template BOOL CMap::RemoveKey(ARG_KEY key) // remove key - return TRUE if removed { //Find the key in the List... POSITION pos = GetPosition(key); if(pos == NULL) return FALSE; //Remove this Node... m_listValues.RemoveAt(pos); return TRUE; } ///////////////////////////////////////////////////////////////////////////// // CMap::SetAt // ///////////////////////////////////////////////////////////////////////////// template BOOL CMap::SetAt(ARG_KEY key, ARG_VALUE value) { //Try to find this key... POSITION pos = GetPosition(key); if(pos) { //Found already, just change the value... AssocFromPos(pos)->m_value = value; } else { //Not found, just need to add to the list... CAssoc* pCAssoc = new CAssoc; if(!pCAssoc) return FALSE; pCAssoc->m_key = key; pCAssoc->m_value = value; //Add it to the list... m_listValues.AddTail(pCAssoc); } return TRUE; } ///////////////////////////////////////////////////////////////////////////// // CMap::GetNextAssoc // ///////////////////////////////////////////////////////////////////////////// template void CMap::GetNextAssoc(POSITION& rNextPosition, KEY& rKey, VALUE& rValue) { if(rNextPosition) { CAssoc* pCAssoc = AssocFromPos(rNextPosition); rKey = pCAssoc->m_key; rValue = pCAssoc->m_value; //Update the position to the next item, for iteration... m_listValues.GetNext(rNextPosition); } } ///////////////////////////////////////////////////////////////////////////// // CVector // ///////////////////////////////////////////////////////////////////////////// template class CVector { public: //constructors CVector(IDX ulLowerBound = 0, IDX ulGrowSize = 10); virtual ~CVector(); //array modifying operations virtual TYPE* AddElement(TYPE element); virtual void RemoveAll(); //insertion deletion routines virtual TYPE* InsertAt(IDX iIndex, TYPE element); // Insert element at index virtual void RemoveAt(IDX iIndex); // Remove element at index //Attaching virtual void Attach(CVector& rVector); virtual void Attach(IDX cElements, TYPE* rgElements); virtual void Detach(IDX* pcElements, TYPE** prgElements); //Array-like methods virtual TYPE& GetElement(IDX iIndex); virtual TYPE& operator[](IDX iIndex) { return GetElement(iIndex); } //informational methods virtual const IDX GetCount() { return m_cElements; } virtual const TYPE* GetElements() const { return m_rgElements; } protected: //data IDX m_ulMaxSize; // MAX sizeof array IDX m_ulGrowSize; // Size to Grow for each allocation IDX m_ulLowerBound; // LowerBound of array IDX m_cElements; // Number of elements TYPE* m_rgElements; // Array of elements }; ///////////////////////////////////////////////////////////////////////////// // CVector::CVector // ///////////////////////////////////////////////////////////////////////////// template CVector::CVector(IDX ulLowerBound, IDX ulGrowSize) { //construct the array m_ulLowerBound = ulLowerBound; m_ulMaxSize = 0; m_ulGrowSize = ulGrowSize; m_cElements = 0; m_rgElements = NULL; } ///////////////////////////////////////////////////////////////////////////// // CVector::~CVector // ///////////////////////////////////////////////////////////////////////////// template CVector::~CVector() { //Delete the Array CoTaskMemFree(m_rgElements); m_rgElements = NULL; m_cElements = 0; } /////////////////////////////////////////////////////////////// // TPropSets::Attach // /////////////////////////////////////////////////////////////// template void CVector::Attach(CVector& rVector) { //Detach from passed in object IDX cElements = 0; TYPE* rgElements = NULL; rVector.Detach(&cElements, &rgElements); //Now Attch them to our object Attach(cElements, rgElements); } /////////////////////////////////////////////////////////////// // CVector::Attach // /////////////////////////////////////////////////////////////// template void CVector::Attach(IDX cElements, TYPE* rgElements) { RemoveAll(); CoTaskMemFree(m_rgElements); m_ulMaxSize = cElements; m_cElements = cElements; m_rgElements = rgElements; } /////////////////////////////////////////////////////////////// // CVector::Detach // /////////////////////////////////////////////////////////////// template void CVector::Detach(IDX* pcElements, TYPE** prgElements) { ASSERT(pcElements); ASSERT(prgElements); //Give our elements to the user... *pcElements = m_cElements; *prgElements = m_rgElements; //We are done with them (consumer owns them...) m_ulMaxSize = 0; m_cElements = 0; m_rgElements = NULL; } ///////////////////////////////////////////////////////////////////////////// // CVector::RemoveAll // ///////////////////////////////////////////////////////////////////////////// template void CVector::RemoveAll() { //NOTE: We don't actually free the array, for optmizations. //We just reset the count of elements, and m_ulMaxSize records the amount of //memory that can be reused without needing reallocations... m_cElements = 0; } ///////////////////////////////////////////////////////////////////////////// // CVector::RemoveAt // ///////////////////////////////////////////////////////////////////////////// template void CVector::RemoveAt(IDX iIndex) { //Remove CVector[position] iIndex -= m_ulLowerBound; ASSERT(iIndex < m_cElements); //May Need to shift the entire array after this element up //If it is not the last row in the vector if(iIndex < m_cElements) memmove(m_rgElements + iIndex, m_rgElements + iIndex + 1, (m_cElements-iIndex) * sizeof(TYPE)); m_cElements--; } ///////////////////////////////////////////////////////////////////////////// // CVector::AddElement // ///////////////////////////////////////////////////////////////////////////// template TYPE* CVector::AddElement(TYPE element) { //Add an element to the end of the list //Just delegate out to our InsertAt method, since we may need //to enlarge the buffer size... return InsertAt(m_cElements + m_ulLowerBound, element); } ///////////////////////////////////////////////////////////////////////////// // CVector::InsertAt // ///////////////////////////////////////////////////////////////////////////// template TYPE* CVector::InsertAt(IDX iIndex, TYPE element) { iIndex -= m_ulLowerBound; ASSERT(iIndex <= m_cElements); //Could be adding at end //May need to alloc more room (if needed) if(m_cElements == m_ulMaxSize) { m_ulMaxSize += 1 + m_ulGrowSize; m_rgElements = (TYPE*)CoTaskMemRealloc(m_rgElements, m_ulMaxSize * sizeof(TYPE)); //Check Allocation if(!m_rgElements) return NULL; } //At this point we need an array... ASSERT(m_rgElements); //May Need to shift the entire array after this element down //If not inserting at the end of the list if(iIndex < m_cElements) memmove(&m_rgElements[iIndex+1], &m_rgElements[iIndex], (m_cElements-iIndex) * sizeof(TYPE)); //Set new value m_rgElements[iIndex] = element; m_cElements++; return &m_rgElements[iIndex]; } ///////////////////////////////////////////////////////////////////////////// // CVector::GetElements // ///////////////////////////////////////////////////////////////////////////// template TYPE& CVector::GetElement(IDX iIndex) { iIndex -= m_ulLowerBound; ASSERT(m_rgElements && iIndex < m_cElements); return m_rgElements[iIndex]; } ///////////////////////////////////////////////////////////////////////////// // CVectorEx // // NOTE: This Vector class requires the operator== to be defined for the type ///////////////////////////////////////////////////////////////////////////// template class CVectorEx : public CVector { public: //constructors CVectorEx(IDX ulLowerBound = 0, IDX ulGrowSize = 10); virtual ~CVectorEx(); //Helpers - require the operator== to be defined virtual void RemoveElement(TYPE element); virtual IDX FindElement(TYPE element); // Find index of element protected: //data }; ///////////////////////////////////////////////////////////////////////////// // CVectorEx::CVectorEx // ///////////////////////////////////////////////////////////////////////////// template CVectorEx::CVectorEx(IDX ulLowerBound, IDX ulGrowSize) : CVector(ulLowerBound, ulGrowSize) { } ///////////////////////////////////////////////////////////////////////////// // CVectorEx::~CVectorEx // ///////////////////////////////////////////////////////////////////////////// template CVectorEx::~CVectorEx() { } ///////////////////////////////////////////////////////////////////////////// // CVectorEx::RemoveElement // ///////////////////////////////////////////////////////////////////////////// template void CVectorEx::RemoveElement(TYPE element) { //Try to find the index of the element IDX iIndex = FindElement(element); //Deletgate to our RemoveAt method RemoveAt(iIndex); } ///////////////////////////////////////////////////////////////////////////// // CVectorEx::FindElement // ///////////////////////////////////////////////////////////////////////////// template IDX CVectorEx::FindElement(TYPE element) { //return pointer to found element for(IDX i=0; i