windows-nt/Source/XPSP1/NT/admin/wmi/wbem/winmgmt/esscli/evaltree.inl
2020-09-26 16:20:57 +08:00

996 lines
27 KiB
C++

/*++
Copyright (C) 1996-1999 Microsoft Corporation
Module Name:
DUMBNODE.H
Abstract:
WBEM Evaluation Tree
History:
--*/
template<class TPropType>
CFullCompareNode<TPropType>::CFullCompareNode(
const CFullCompareNode<TPropType>& Other,
BOOL bChildren)
: CPropertyNode(Other, FALSE), // copy without children
m_pRightMost(NULL)
{
if(bChildren)
{
// Need to copy the children. Iterate over our test point array
// =============================================================
for(TConstTestPointIterator it = Other.m_aTestPoints.Begin();
it != Other.m_aTestPoints.End(); it++)
{
CTestPoint<TPropType> NewPoint;
NewPoint.m_Test = it->m_Test;
// Make copies of the child branches
// =================================
NewPoint.m_pLeftOf = CEvalNode::CloneNode(it->m_pLeftOf);
NewPoint.m_pAt = CEvalNode::CloneNode(it->m_pAt);
// Add the test point to the array
// ===============================
m_aTestPoints.Append(NewPoint);
}
// Copy right-most
// ===============
m_pRightMost = CEvalNode::CloneNode(Other.m_pRightMost);
}
}
template<class TPropType>
HRESULT CFullCompareNode<TPropType>::SetTest(VARIANT& v)
{
try
{
CTokenValue Value;
if(!Value.SetVariant(v))
return WBEM_E_OUT_OF_MEMORY;
m_aTestPoints.Begin()->m_Test = Value;
return WBEM_S_NO_ERROR;
}
catch(CX_MemoryException)
{
return WBEM_E_OUT_OF_MEMORY;
}
}
template<class TPropType>
CFullCompareNode<TPropType>::~CFullCompareNode()
{
delete m_pRightMost;
}
template<class TPropType>
HRESULT CFullCompareNode<TPropType>::InsertMatching(
TTestPointIterator it,
TTestPointIterator it2, TTestPointIterator& itLast,
int nOp, CContextMetaData* pNamespace,
CImplicationList& Implications, bool bDeleteArg2)
{
//=====================================================================
// 'it' points to the node in 'this' that has the same value as the one
// 'it2' points to in pArg2. 'itLast' points to the last unhandled node
// in 'this' (looking left from 'it').
//=====================================================================
CEvalNode* pNew;
HRESULT hres;
// Merge our at-values
// ===================
hres = CEvalTree::Combine(it->m_pAt, it2->m_pAt, nOp, pNamespace,
Implications, true, bDeleteArg2, &pNew);
if(FAILED(hres))
return hres;
if(bDeleteArg2)
it2->m_pAt = NULL;
it->m_pAt = pNew;
// Merge our left-ofs
// ==================
hres = CEvalTree::Combine(it->m_pLeftOf, it2->m_pLeftOf, nOp, pNamespace,
Implications, true, bDeleteArg2, &pNew);
if(FAILED(hres))
return hres;
if(bDeleteArg2)
it2->m_pLeftOf = NULL;
it->m_pLeftOf = pNew;
//
// At this point, we need to merge the LeftOf of it2 with all the branches
// in this that are between the current insertion point and the previous
// insertion point. However, we do not need to do this if it2's LeftOf node
// is a noop for this operation (e.g. TRUE for an AND)
//
if(it != itLast && !CEvalNode::IsNoop(it2->m_pLeftOf, nOp))
{
hres = CombineWithBranchesToLeft(it, itLast, it2->m_pLeftOf, nOp,
pNamespace, Implications);
if(FAILED(hres))
return hres;
}
// Move first unhandled iterator to the node beyond itLast
// =======================================================
itLast = it;
itLast++;
return WBEM_S_NO_ERROR;
}
template<class TPropType>
HRESULT CFullCompareNode<TPropType>::InsertLess(
TTestPointIterator it,
TTestPointIterator it2, TTestPointIterator& itLast,
int nOp, CContextMetaData* pNamespace,
CImplicationList& Implications, bool bDeleteArg2)
{
//
// 'it' points to the node in 'this' that has a slightly larger value than
// the one 'it2' points to in pArg2. 'itLast' points to the last unhandled
// node in 'this' (looking left from 'it').
//
HRESULT hres;
// Check if 'it' is point to the end of the list --- in that case it's
// "left-of" is actually right-most
// ===================================================================
CEvalNode* pItLeft = NULL;
if(it == m_aTestPoints.End())
pItLeft = m_pRightMost;
else
pItLeft = it->m_pLeftOf;
// First of all, we need to insert the node at it2 into 'this' list, just
// before 'it'.
// ======================================================================
CTestPoint<TPropType> NewNode = *it2;
// It's at branch is the combination of our left and arg2 at
// =========================================================
hres = CEvalTree::Combine(pItLeft, it2->m_pAt, nOp, pNamespace,
Implications, false, bDeleteArg2, &NewNode.m_pAt);
if(FAILED(hres))
return hres;
if(bDeleteArg2)
it2->m_pAt = NULL;
// It's left-of branch is the combination of our left and arg2 left
// ================================================================
// We can reuse it2->Left iff every node left of 'it' in 'this' has been
// handled
// =====================================================================
bool bDeleteIt2Left = (bDeleteArg2 && (it == itLast));
hres = CEvalTree::Combine(pItLeft, it2->m_pLeftOf, nOp, pNamespace,
Implications, false, bDeleteIt2Left, &NewNode.m_pLeftOf);
if(FAILED(hres))
return hres;
if(bDeleteIt2Left)
it2->m_pLeftOf = NULL;
// IMPORTANT: Once we insert the new node, all the iterators into 'this'
// will be invalidated --- that includes it and itLast. So, we need to do
// out left-walk before we actually insert.
// ===================================================
//
// At this point, we need to merge the LeftOf of it2 with all the branches
// in this that are between the current insertion point and the previous
// insertion point. However, we do not need to do this if it2's LeftOf node
// is a noop for this operation (e.g. TRUE for an AND)
//
if(it != itLast && !CEvalNode::IsNoop(it2->m_pLeftOf, nOp))
{
//
// Note to self: bDeleteIt2Left could not have been true, so
// it2->m_pLeftOf could not have been deleted. We are OK here
//
hres = CombineWithBranchesToLeft(it, itLast, it2->m_pLeftOf, nOp,
pNamespace, Implications);
if(FAILED(hres))
return hres;
}
// Now we can actually insert
// ==========================
TTestPointIterator itNew = m_aTestPoints.Insert(it, NewNode);
// Move first unhandled iterator to the node just right of insertion
// =================================================================
itLast = itNew;
itLast++;
return WBEM_S_NO_ERROR;
}
template<class TPropType>
HRESULT CFullCompareNode<TPropType>::CombineWithBranchesToLeft(
TTestPointIterator itWalk, TTestPointIterator itLast,
CEvalNode* pArg2,
int nOp, CContextMetaData* pNamespace,
CImplicationList& Implications)
{
HRESULT hres;
CEvalNode* pNew = NULL;
// Walk left until we reach the first unhandled node
// =================================================
do
{
if(itWalk == m_aTestPoints.Begin())
break;
itWalk--;
// Merge at-value
// ==============
hres = CEvalTree::Combine(itWalk->m_pAt, pArg2, nOp,
pNamespace, Implications, true, false, &pNew);
if(FAILED(hres))
return hres;
itWalk->m_pAt = pNew;
// Merge left-ofs
// ==============
hres = CEvalTree::Combine(itWalk->m_pLeftOf, pArg2, nOp,
pNamespace, Implications, true, false, &pNew);
if(FAILED(hres))
return hres;
itWalk->m_pLeftOf = pNew;
}
while(itWalk != itLast);
return WBEM_S_NO_ERROR;
}
template<class TPropType>
HRESULT CFullCompareNode<TPropType>::CombineBranchesWith(
CBranchingNode* pRawArg2, int nOp, CContextMetaData* pNamespace,
CImplicationList& Implications,
bool bDeleteThis, bool bDeleteArg2, CEvalNode** ppRes)
{
HRESULT hres;
CFullCompareNode<TPropType>* pArg2 = (CFullCompareNode<TPropType>*)pRawArg2;
*ppRes = NULL;
// Check which one is larger
// =========================
if(m_aTestPoints.GetSize() < pArg2->m_aTestPoints.GetSize())
{
return pArg2->CombineBranchesWith(this, FlipEvalOp(nOp), pNamespace,
Implications, bDeleteArg2, bDeleteThis, ppRes);
}
if(!bDeleteThis)
{
// Damn. Clone.
// ============
return ((CFullCompareNode<TPropType>*)Clone())->CombineBranchesWith(
pRawArg2, nOp, pNamespace, Implications, true, // reuse clone!
bDeleteArg2, ppRes);
}
CEvalNode* pNew = NULL;
TTestPointIterator itLast = m_aTestPoints.Begin();
//
// itLast points to the left-most location in our list of test points that
// we have not considered yet --- it is guaranteed that any further
// insertions from the second list will occur after this point
//
//
// it2, on the other hand, iterates simply over the second list of test
// points, inserting each one into the combined list one by one
//
for(TTestPointIterator it2 = pArg2->m_aTestPoints.Begin();
it2 != pArg2->m_aTestPoints.End(); it2++)
{
//
// First, we search for the location in our list of test points of the
// insertion point for the value of it2. bMatch is set to true if the
// same test point exists, and false if it does not and the returned
// iterator points to the element to the right of the insertion point.
//
TTestPointIterator it;
bool bMatch = m_aTestPoints.Find(it2->m_Test, &it);
if(bMatch)
{
hres = InsertMatching(it, it2, itLast, nOp, pNamespace,
Implications, bDeleteArg2);
}
else
{
hres = InsertLess(it, it2, itLast, nOp, pNamespace,
Implications, bDeleteArg2);
// invalidates 'it'!
}
if(FAILED(hres))
return hres;
}
//
// At this point, we need to merge the RightMost of arg2 with all the
// branches in this that come after the last insertion point.
// However, we do not need to do this if arg2's RightMost node
// is a noop for this operation (e.g. TRUE for an AND)
//
if(itLast != m_aTestPoints.End() &&
!CEvalNode::IsNoop(pArg2->m_pRightMost, nOp))
{
hres = CombineWithBranchesToLeft(m_aTestPoints.End(), itLast,
pArg2->m_pRightMost, nOp, pNamespace, Implications);
if(FAILED(hres))
return hres;
}
hres = CEvalTree::Combine(m_pRightMost, pArg2->m_pRightMost, nOp,
pNamespace, Implications, true, bDeleteArg2,
&pNew);
if(FAILED(hres))
return hres;
m_pRightMost = pNew;
if(bDeleteArg2)
pArg2->m_pRightMost = NULL;
// Merge the nulls
// ===============
CEvalTree::Combine(m_pNullBranch, pArg2->m_pNullBranch, nOp,
pNamespace, Implications, true, bDeleteArg2,
&pNew);
m_pNullBranch = pNew;
// Reset them in deleted versions
// ==============================
if(bDeleteArg2)
pArg2->m_pNullBranch = NULL;
// Delete what needs deleting
// ==========================
if(bDeleteArg2)
delete pArg2;
*ppRes = this;
return WBEM_S_NO_ERROR;
}
template<class TPropType>
HRESULT CFullCompareNode<TPropType>::CombineInOrderWith(CEvalNode* pArg2,
int nOp, CContextMetaData* pNamespace,
CImplicationList& OrigImplications,
bool bDeleteThis, bool bDeleteArg2,
CEvalNode** ppRes)
{
HRESULT hres;
*ppRes = Clone();
if(*ppRes == NULL)
return WBEM_E_OUT_OF_MEMORY;
CFullCompareNode<TPropType>* pNew = (CFullCompareNode<TPropType>*)*ppRes;
try
{
CImplicationList Implications(OrigImplications);
hres = pNew->AdjustCompile(pNamespace, Implications);
if(FAILED(hres))
return hres;
CEvalNode* pNewBranch = NULL;
for(TTestPointIterator it = pNew->m_aTestPoints.Begin();
it != pNew->m_aTestPoints.End(); it++)
{
// Combine our At-branch with pArg2
// ================================
hres = CEvalTree::Combine(it->m_pAt, pArg2, nOp, pNamespace,
Implications, true, false, &pNewBranch);
if(FAILED(hres))
{
delete pNew;
return hres;
}
it->m_pAt = pNewBranch;
// Now do the same for our left-of branch
// ======================================
hres = CEvalTree::Combine(it->m_pLeftOf, pArg2, nOp, pNamespace,
Implications, true, false, &pNewBranch);
if(FAILED(hres))
{
delete pNew;
return hres;
}
it->m_pLeftOf = pNewBranch;
}
hres = CEvalTree::Combine(pNew->m_pRightMost, pArg2, nOp, pNamespace,
Implications, true, false, &pNewBranch);
if(FAILED(hres))
{
delete pNew;
return hres;
}
pNew->m_pRightMost = pNewBranch;
hres = CEvalTree::Combine(pNew->m_pNullBranch, pArg2, nOp, pNamespace,
Implications, true, false, &pNewBranch);
if(FAILED(hres))
{
delete pNew;
return hres;
}
pNew->m_pNullBranch = pNewBranch;
if(bDeleteThis)
delete this;
if(bDeleteArg2)
delete pArg2;
return WBEM_S_NO_ERROR;
}
catch(CX_MemoryException)
{
return WBEM_E_OUT_OF_MEMORY;
}
}
template<class TPropType>
int CFullCompareNode<TPropType>::SubCompare(CEvalNode* pRawOther)
{
CFullCompareNode<TPropType>* pOther =
(CFullCompareNode<TPropType>*)pRawOther;
// Compare handles
// ===============
int nCompare;
nCompare = m_lPropHandle - pOther->m_lPropHandle;
if(nCompare)
return nCompare;
// Compare array sizes
// ===================
nCompare = m_aTestPoints.GetSize() - pOther->m_aTestPoints.GetSize();
if(nCompare)
return nCompare;
// Compare all points
// ==================
TTestPointIterator it;
TTestPointIterator itOther;
for(it = m_aTestPoints.Begin(), itOther = pOther->m_aTestPoints.Begin();
it != m_aTestPoints.End(); it++, itOther++)
{
if(it->m_Test < itOther->m_Test)
return -1;
else if(it->m_Test > itOther->m_Test)
return 1;
}
// Compare all branches
// ====================
for(it = m_aTestPoints.Begin(), itOther = pOther->m_aTestPoints.Begin();
it != m_aTestPoints.End(); it++, itOther++)
{
nCompare = CEvalTree::Compare(it->m_pLeftOf, itOther->m_pLeftOf);
if(nCompare)
return nCompare;
nCompare = CEvalTree::Compare(it->m_pAt, itOther->m_pAt);
if(nCompare)
return nCompare;
}
return 0;
}
template<class TPropType>
HRESULT CFullCompareNode<TPropType>::OptimizeSelf()
{
TTestPointIterator it = m_aTestPoints.Begin();
while(it != m_aTestPoints.End())
{
TTestPointIterator itPrev = it;
it++;
CEvalNode** ppLeft = &itPrev->m_pLeftOf;
CEvalNode** ppMiddle = &itPrev->m_pAt;
CEvalNode** ppRight = it != m_aTestPoints.End() ?
&it->m_pLeftOf : &m_pRightMost;
//
// compare all three test point nodes. If all the same then we
// can optimize the test point out. Also, two nodes are treated
// the same if at least one of them is the invalid node.
//
if ( !CEvalNode::IsInvalid( *ppLeft ) )
{
if ( !CEvalNode::IsInvalid( *ppMiddle ) )
{
if( CEvalTree::Compare( *ppLeft, *ppMiddle ) != 0 )
{
continue;
}
}
else
{
ppMiddle = ppLeft;
}
}
if ( !CEvalNode::IsInvalid( *ppMiddle ) )
{
if ( !CEvalNode::IsInvalid( *ppRight ) )
{
if( CEvalTree::Compare( *ppMiddle, *ppRight ) != 0 )
{
continue;
}
}
//
// we're going to optimize the test point out, but first
// make sure to set rightmost to point to the middle branch.
// Make sure to unhook appropriate pointers before removing
// the test node, since it owns the memory for them.
//
delete *ppRight;
*ppRight = *ppMiddle;
*ppMiddle = NULL;
}
//
// optimize the test point out.
//
it = m_aTestPoints.Remove( itPrev );
}
return S_OK;
}
template<class TPropType>
DWORD CFullCompareNode<TPropType>::ApplyPredicate(CLeafPredicate* pPred)
{
DWORD dwRes;
for(TTestPointIterator it = m_aTestPoints.Begin();
it != m_aTestPoints.End(); it++)
{
if (it->m_pLeftOf)
{
dwRes = it->m_pLeftOf->ApplyPredicate(pPred);
if(dwRes & WBEM_DISPOSITION_FLAG_DELETE)
{
delete it->m_pLeftOf;
it->m_pLeftOf = NULL;
}
else if ( dwRes & WBEM_DISPOSITION_FLAG_INVALIDATE )
{
delete it->m_pLeftOf;
it->m_pLeftOf = CValueNode::GetStandardInvalid();
}
}
if (it->m_pAt)
{
dwRes = it->m_pAt->ApplyPredicate(pPred);
if(dwRes & WBEM_DISPOSITION_FLAG_DELETE)
{
delete it->m_pAt;
it->m_pAt = NULL;
}
else if ( dwRes & WBEM_DISPOSITION_FLAG_INVALIDATE )
{
delete it->m_pAt;
it->m_pAt = CValueNode::GetStandardInvalid();
}
}
}
if (m_pRightMost)
{
dwRes = m_pRightMost->ApplyPredicate(pPred);
if(dwRes & WBEM_DISPOSITION_FLAG_DELETE)
{
delete m_pRightMost;
m_pRightMost = NULL;
}
else if ( dwRes & WBEM_DISPOSITION_FLAG_INVALIDATE )
{
delete m_pRightMost;
m_pRightMost = CValueNode::GetStandardInvalid();
}
}
return CBranchingNode::ApplyPredicate(pPred);
}
template<class TPropType>
HRESULT CFullCompareNode<TPropType>::Optimize(CContextMetaData* pNamespace,
CEvalNode** ppNew)
{
CEvalNode* pNew = NULL;
HRESULT hres;
// Optimize all branches
// =====================
for(TTestPointIterator it = m_aTestPoints.Begin();
it != m_aTestPoints.End(); it++)
{
if (it->m_pLeftOf)
{
hres = it->m_pLeftOf->Optimize(pNamespace, &pNew);
if(FAILED(hres))
return hres;
if(pNew != it->m_pLeftOf)
{
delete it->m_pLeftOf;
it->m_pLeftOf = pNew;
}
}
if (it->m_pAt)
{
hres = it->m_pAt->Optimize(pNamespace, &pNew);
if(FAILED(hres))
return hres;
if(pNew != it->m_pAt)
{
delete it->m_pAt;
it->m_pAt = pNew;
}
}
}
if (m_pRightMost)
{
hres = m_pRightMost->Optimize(pNamespace, &pNew);
if(FAILED(hres))
return hres;
if(pNew != m_pRightMost)
{
delete m_pRightMost;
m_pRightMost = pNew;
}
}
if (m_pNullBranch)
{
hres = m_pNullBranch->Optimize(pNamespace, &pNew);
if(FAILED(hres))
return hres;
if(pNew != m_pNullBranch)
{
delete m_pNullBranch;
m_pNullBranch = pNew;
}
}
// Optimize ourselves
// ==================
hres = OptimizeSelf();
if(FAILED(hres))
return hres;
*ppNew = this;
//
// Check if this node has become superflous
//
if( m_aTestPoints.GetSize() == 0 )
{
if ( !CEvalNode::IsInvalid( m_pRightMost ) )
{
if ( !CEvalNode::IsInvalid( m_pNullBranch ) )
{
if ( CEvalTree::Compare(m_pNullBranch, m_pRightMost) == 0 )
{
//
// both the null and rightmost are the same. Optimize
// this node out and return the rightmost branch.
//
*ppNew = m_pRightMost;
//
// Untie m_pRightMost (so it is not deleted when we are)
//
m_pRightMost = NULL;
}
}
else if ( m_pRightMost == NULL )
{
//
// the right branch is false and the null branch is invalid.
// Optimize this node to false.
//
*ppNew = NULL;
}
}
else if ( m_pNullBranch == NULL )
{
//
// the null branch is false and the rightmost is invalid.
// Optimize this node to false.
//
*ppNew = NULL;
}
else if ( CEvalNode::IsInvalid( m_pNullBranch ) )
{
//
// both are invalid, but we can't invalidate the whole node
// because we're not sure what we optimized out in the test
// points, so just optimize this node to false.
//
*ppNew = NULL;
}
}
return S_OK;
}
template<class TPropType>
HRESULT CFullCompareNode<TPropType>::SetNullTest(int nOperator)
{
if(nOperator == QL1_OPERATOR_EQUALS)
{
m_pRightMost = CValueNode::GetStandardFalse();
CEvalNode* pNode = CValueNode::GetStandardTrue();
if(pNode == NULL)
return WBEM_E_OUT_OF_MEMORY;
SetNullBranch(pNode);
}
else if(nOperator == QL1_OPERATOR_NOTEQUALS)
{
m_pRightMost = CValueNode::GetStandardTrue();
if(m_pRightMost == NULL)
return WBEM_E_OUT_OF_MEMORY;
SetNullBranch(CValueNode::GetStandardFalse());
}
else
return WBEM_E_INVALID_QUERY;
return WBEM_S_NO_ERROR;
}
template<class TPropType>
HRESULT CFullCompareNode<TPropType>::SetOperator(int nOperator)
{
HRESULT hr = WBEM_S_NO_ERROR;
#define GET_STD_TRUE CValueNode::GetStandardTrue()
#define GET_STD_FALSE CValueNode::GetStandardFalse()
#define SET_TRUE(NODE) { \
NODE = GET_STD_TRUE; \
if(NODE == NULL) { \
hr = WBEM_E_OUT_OF_MEMORY; \
break; } }
#define SET_FALSE(NODE) {NODE = GET_STD_FALSE;}
CTestPoint<TPropType> NewNode;
switch(nOperator)
{
case QL1_OPERATOR_EQUALS:
SET_FALSE(NewNode.m_pLeftOf);
SET_TRUE(NewNode.m_pAt);
SET_FALSE(m_pRightMost);
break;
case QL1_OPERATOR_NOTEQUALS:
SET_TRUE(NewNode.m_pLeftOf);
SET_FALSE(NewNode.m_pAt);
SET_TRUE(m_pRightMost);
break;
case QL1_OPERATOR_LESS:
SET_TRUE(NewNode.m_pLeftOf);
SET_FALSE(NewNode.m_pAt);
SET_FALSE(m_pRightMost);
break;
case QL1_OPERATOR_GREATER:
SET_FALSE(NewNode.m_pLeftOf);
SET_FALSE(NewNode.m_pAt);
SET_TRUE(m_pRightMost);
break;
case QL1_OPERATOR_LESSOREQUALS:
SET_TRUE(NewNode.m_pLeftOf);
SET_TRUE(NewNode.m_pAt);
SET_FALSE(m_pRightMost);
break;
case QL1_OPERATOR_GREATEROREQUALS:
SET_FALSE(NewNode.m_pLeftOf);
SET_TRUE(NewNode.m_pAt);
SET_TRUE(m_pRightMost);
break;
default:
hr = WBEM_E_CRITICAL_ERROR;
}
if ( SUCCEEDED(hr) )
{
m_aTestPoints.Append(NewNode);
}
else
{
NewNode.Destruct();
}
return hr;
}
//******************************************************************************
//******************************************************************************
// SCALAR PROPERTY NODE
//******************************************************************************
//******************************************************************************
template<class TPropType>
HRESULT CScalarPropNode<TPropType>::Evaluate(CObjectInfo& ObjInfo,
INTERNAL CEvalNode** ppNext)
{
HRESULT hres;
_IWmiObject* pObj;
hres = GetContainerObject(ObjInfo, &pObj);
if(FAILED(hres)) return hres;
// Get the property from the object
// ================================
long lRead;
TPropType Value;
hres = pObj->ReadPropertyValue(m_lPropHandle, sizeof(TPropType),
&lRead, (BYTE*)&Value);
if( S_OK != hres )
{
if(hres == WBEM_S_FALSE)
{
*ppNext = m_pNullBranch;
return WBEM_S_NO_ERROR;
}
else
{
return hres;
}
}
// Search for the value
// ====================
TTestPointIterator it;
bool bMatch = m_aTestPoints.Find(Value, &it);
if(bMatch)
*ppNext = it->m_pAt;
else if(it == m_aTestPoints.End())
*ppNext = m_pRightMost;
else
*ppNext = it->m_pLeftOf;
return WBEM_S_NO_ERROR;
}
template<class TPropType>
void CScalarPropNode<TPropType>::Dump(FILE* f, int nOffset)
{
CBranchingNode::Dump(f, nOffset);
PrintOffset(f, nOffset);
fprintf(f, "LastPropName = (0x%x), size=%d\n",
m_lPropHandle, sizeof(TPropType));
TConstTestPointIterator it;
for(it = m_aTestPoints.Begin();
it != m_aTestPoints.End(); it++)
{
PrintOffset(f, nOffset);
if (it != m_aTestPoints.Begin())
{
TConstTestPointIterator itPrev(it);
itPrev--;
fprintf(f, "%d < ", (int)(itPrev->m_Test));
}
fprintf(f, "X < %d\n", (int)(it->m_Test));
DumpNode(f, nOffset+1, it->m_pLeftOf);
PrintOffset(f, nOffset);
fprintf(f, "X = %d\n", (int)(it->m_Test));
DumpNode(f, nOffset+1, it->m_pAt);
}
PrintOffset(f, nOffset);
if (it != m_aTestPoints.Begin())
{
TConstTestPointIterator itPrev(it);
itPrev--;
fprintf(f, "X > %d\n", (int)(itPrev->m_Test));
}
else
fprintf(f, "ANY\n");
DumpNode(f, nOffset+1, m_pRightMost);
PrintOffset(f, nOffset);
fprintf(f, "NULL->\n");
DumpNode(f, nOffset+1, m_pNullBranch);
}