|
|
/*---------------------------------------------------------------------------
File: VSet.cpp
Comments: Implementation of IVarSet interface.
(c) Copyright 1995-1998, Mission Critical Software, Inc., All Rights Reserved Proprietary and confidential to Mission Critical Software, Inc.
REVISION LOG ENTRY Revision By: Christy Boles Revised on 11/19/98 19:44:06
--------------------------------------------------------------------------- */
// VSet.cpp : Implementation of CVSet
#include "stdafx.h"
#ifdef STRIPPED_VARSET
#include "NoMcs.h"
#include <comdef.h>
#include "Err.hpp"
#include "Varset.h"
#else
#endif
#include "VarSetI.h"
#include "VSet.h"
#include "VarMap.h"
#include "DotStr.hpp"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__; #endif
/////////////////////////////////////////////////////////////////////////////
// CVSet
/////////////////////////////////////////////////////////////////////
// IVarSet
/////////////////////////////////////////////////////////////////////
// Gets the number of items in the map and all sub-maps
STDMETHODIMP CVSet::get_Count(/* [retval][out] */long* retval) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get_Count"); if (retval == NULL) { MCSVERIFYSZ(FALSE,"get_Count: output pointer was null, returning E_POINTER"); return E_POINTER; }
m_cs.Lock(); *retval = m_nItems; MCSASSERTSZ(! m_nItems || m_nItems == m_data->CountItems() - (m_data->HasData()?0:1),"get_Count:Item count consistency check failed."); m_cs.Unlock(); return S_OK; }
STDMETHODIMP CVSet::get_NumChildren(/* [in] */BSTR parentKey,/* [out,retval] */long*count) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get_NumChildren");
HRESULT hr = S_OK; CVarData * pVar; CString parent;
parent = parentKey; if ( count == NULL ) { MCSVERIFYSZ(FALSE,"get_NumChildren: output pointer was null, returning E_POINTER"); hr = E_POINTER; } else { m_cs.Lock(); pVar = GetItem(parent,FALSE); if ( pVar ) { if ( pVar->HasChildren() ) { (*count) = pVar->GetChildren()->GetCount(); } else { (*count) = 0; } } else { // The parent key does not exist
(*count) = 0; } m_cs.Unlock(); } return hr; }
// Adds or changes a value in the map
STDMETHODIMP CVSet::putObject(/* [in] */BSTR property,/* [in] */VARIANT value) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::putObject"); CVarData * pVar = NULL; HRESULT hr = S_OK;
if ( m_Restrictions & VARSET_RESTRICT_NOCHANGEDATA ) { hr = E_ACCESSDENIED; } else { m_cs.Lock(); m_bNeedToSave = TRUE; pVar = GetItem(property,TRUE); if ( pVar ) { MC_LOG("set value for " << McString::String(property)); m_nItems+=pVar->SetData("",&value,FALSE,&hr); } else { MCSASSERTSZ(FALSE,"VarSet internal error creating or retrieving node"); // GetItem failed - cannot add item to property
hr = E_FAIL; } m_cs.Unlock(); } return hr; }
// Adds or changes a value in the map
STDMETHODIMP CVSet::put(/* [in] */BSTR property,/* [in] */VARIANT value) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::put"); CVarData * pVar = NULL; HRESULT hr = S_OK;
if ( m_Restrictions & VARSET_RESTRICT_NOCHANGEDATA ) { hr = E_ACCESSDENIED; } else { m_cs.Lock(); m_bNeedToSave = TRUE; pVar = GetItem(property,TRUE); if ( pVar ) { MC_LOG("set value for " << McString::String(property)); m_nItems+=pVar->SetData("",&value,TRUE,&hr); } else { MCSASSERTSZ(FALSE,"VarSet internal error creating or retrieving node"); // GetItem failed - cannot add item to property
hr = E_FAIL; } m_cs.Unlock(); } return hr; }
CVarData * // ret- pointer to item in varset
CVSet::GetItem( CString str, // in - key to look for
BOOL addToMap, // in - if TRUE, adds the key to the map if it does not exist
CVarData * base // in - starting point
) { MC_LOGBLOCKIF(VARSET_LOGLEVEL_INTERNAL,"CVSet::GetItem"); CVarData * curr = base; CVarData * result = NULL; CDottedString s(str); CString seg; CString next;
if ( ! curr ) { curr = m_data; MC_LOG("No basepoint provided, using root element"); }
if ( str.IsEmpty() ) { result = curr; MC_LOG("Returning current node"); } else { for ( int i = 0 ; curr && i < s.NumSegments(); i++ ) { s.GetSegment(i,seg); MC_LOG("Looking for key segment "<< McString::String(seg) ); curr->SetCaseSensitive(m_CaseSensitive); if ( ! curr->Lookup(seg,result) ) { if ( addToMap ) { MC_LOG(McString::String(seg) << " not found, creating new node"); result = new CVarData; if (!result) break; try { result->SetCaseSensitive(m_CaseSensitive); result->SetIndexed(m_Indexed); curr->SetAt(seg,result); m_nItems++; } catch(...) { delete result; result = NULL; throw; } } else { MC_LOG(McString::String(seg) << " not found, aborting"); result = NULL; break; } } curr = result; } } return result; }
// Retrieves a value from the map
STDMETHODIMP CVSet::get(/* [in] */BSTR property,/* [retval][out] */VARIANT * value) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get");
CVarData * pVar; HRESULT hr = S_OK; CComVariant var; CString s; if (property == NULL ) { MCSVERIFYSZ(FALSE,"CVSet::get - output pointer is NULL, returning E_POINTER"); hr = E_POINTER; } else { m_cs.Lock(); s = property; pVar = GetItem(s); var.Attach(value); if ( pVar ) { MC_LOG("got value for " << McString::String(property)); var.Copy(pVar->GetData()); } else { MC_LOG("CVSet::get " << McString::String(property) << " was not found, returning empty variant"); } // if the item was not found, set the variant to VT_EMPTY
var.Detach(value); m_cs.Unlock(); } return hr; }
STDMETHODIMP CVSet::put_CaseSensitive(/* [in] */BOOL newVal) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::put_CaseSensitive"); HRESULT hr = S_OK;
if ( m_Restrictions & VARSET_RESTRICT_NOCHANGEPROPS ) { hr = E_ACCESSDENIED; } else { m_cs.Lock(); m_bNeedToSave = TRUE; m_CaseSensitive = newVal; m_cs.Unlock(); } return hr; }
STDMETHODIMP CVSet::get_CaseSensitive(/* [retval][out] */BOOL * isCaseSensitive) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get_CaseSensitive"); if ( ! isCaseSensitive ) { MCSVERIFYSZ(FALSE,"CVSet::get_CaseSensitive - output pointer is NULL, returning E_POINTER"); return E_POINTER; } else { m_cs.Lock(); (*isCaseSensitive) = m_CaseSensitive; m_cs.Unlock(); } return S_OK; }
// This function is used to sort the keys being returned from an enum.
int __cdecl SortComVariantStrings(const void * v1, const void * v2) { CComVariant * var1 = (CComVariant*)v1; CComVariant * var2 = (CComVariant*)v2;
if ( var1->vt == VT_BSTR && var2->vt == VT_BSTR ) { return wcscmp(var1->bstrVal,var2->bstrVal); } return 0; }
// This returns an IEnumVARIANT interface. It is used by the VB For Each command.
// This enumerates only the keys, not the values. It is not very efficient, especially for large sets.
STDMETHODIMP CVSet::get__NewEnum(/* [retval][out] */IUnknown** retval) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get_NewEnum"); if (retval == NULL) { MCSVERIFYSZ(FALSE,"CVSet::get_NewEnum - output pointer is NULL, returning E_POINTER"); return E_POINTER; }
// initialize output parameter
(*retval) = NULL;
typedef CComObject<CComEnum<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT, _Copy<VARIANT> > > enumvar;
HRESULT hRes = S_OK;
enumvar * p = new enumvar; if (p == NULL) { MCSVERIFYSZ(FALSE,"CVSet::get_NewEnum - Could not create IEnumVARIANT object"); hRes = E_OUTOFMEMORY; } else { hRes = p->FinalConstruct(); if (hRes == S_OK) { m_cs.Lock(); CVarData * map = m_data; CString start; CString seg; // Build an array of variants to hold the keys
CComVariant * pVars = new CComVariant[m_data->CountItems()+1]; CString key; int offset = 0;
key = _T(""); if ( map->GetData() && map->GetData()->vt != VT_EMPTY ) { pVars[offset] = key; offset++; } if ( map->HasChildren() ) { BuildVariantKeyArray(key,map->GetChildren(),pVars,&offset); } if ( ! m_Indexed ) { // Sort the results
qsort(pVars,offset,(sizeof CComVariant),&SortComVariantStrings); }
hRes = p->Init(pVars, &pVars[offset], NULL,AtlFlagCopy); if (hRes == S_OK) hRes = p->QueryInterface(IID_IUnknown, (void**)retval); delete [] pVars; m_cs.Unlock(); } } if (hRes != S_OK) delete p; return hRes; }
// Helper function for get__NewEnum
// copies all the keys in the map, and all sub-maps, into a CComVariant array.
// the values are then sorted if necessary.
void CVSet::BuildVariantKeyArray( CString prefix, // in - string to tack on to the beginning of each key (used when enumerating subkeys)
CMapStringToVar * map, // in - map containing data
CComVariant * pVars, // i/o- array that will contain all the keys
int * offset // i/o- number of keys copied to pVars (index to use for next insertion)
) { MC_LOGBLOCKIF(VARSET_LOGLEVEL_INTERNAL,"CVSet::BuildVariantKeyArray"); int i; int nItems; CVarData * pObj; CString key; CComVariant var; CString val;
if ( ! map ) return; // no data =>no work to do
nItems = map->GetCount(); if ( ! m_Indexed ) { POSITION pos; pos = map->GetStartPosition(); for ( i = 0 ; pos && i < nItems ; i++ ) { map->GetNextAssoc(pos,key,pObj); if ( ! prefix.IsEmpty() ) { var = prefix + L"." + key; } else { var = key; } // add each key to the array
var.Detach(&pVars[(*offset)]); (*offset)++; if ( pObj->HasChildren() ) { // Recursively do the sub-map
if ( ! prefix.IsEmpty() ) { BuildVariantKeyArray(prefix+L"."+key,pObj->GetChildren(),pVars,offset); } else { BuildVariantKeyArray(key,pObj->GetChildren(),pVars,offset); } } } } else { CIndexItem * item; CIndexTree * index = map->GetIndex();
ASSERT(index); if ( ! index ) return; item = index->GetFirstItem();
for ( i = 0 ; item && i < nItems ; i++ ) { key = item->GetKey(); pObj = item->GetValue();
if ( ! prefix.IsEmpty() ) { var = prefix + L"." + key; } else { var = key; } // add each key to the array
var.Detach(&pVars[(*offset)]); (*offset)++; if ( pObj->HasChildren() ) { // Recursively do the sub-map
if ( ! prefix.IsEmpty() ) { BuildVariantKeyArray(prefix+L"."+key,pObj->GetChildren(),pVars,offset); } else { BuildVariantKeyArray(key,pObj->GetChildren(),pVars,offset); } } item = index->GetNextItem(item); } } }
STDMETHODIMP CVSet::getItems2( /* [in] */VARIANT basepoint, // in - if specified, only children of this node will be enumerated
/* [in] */VARIANT startAfter, // in - the enumeration will begin with the next item in the map following this key.
/* [in] */VARIANT bRecursive, // in - TRUE includes all sub-items, FALSE enumerates one level only.
/* [in] */VARIANT bSize, // in - max number of elements to return (the size of the arrays)
/* [out] */VARIANT * keyVar, // out- array of keys
/* [out] */VARIANT * valVar, // out- array of values
/* [in,out] */VARIANT* nReturned // out- number of items copied
) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::getItems2"); HRESULT hr = S_OK; LONG n = 0; LONG size = bSize.pvarVal->iVal; // TODO: Verify that all the arguments are the correct type!
// Allocate SAFEARRAYs for the keys and values
SAFEARRAY * keys = NULL; SAFEARRAY * values= NULL; _variant_t key; _variant_t val; _variant_t num;
if ( ! keys || !values ) { hr = E_OUTOFMEMORY; } else { hr = getItems(basepoint.bstrVal,startAfter.bstrVal,bRecursive.boolVal,size,&keys,&values,&n); key.vt = VT_ARRAY | VT_VARIANT; key.parray = keys; val.vt = VT_ARRAY | VT_VARIANT; val.parray = values; num.vt = VT_I4; num.lVal = n; (*keyVar) = key.Detach(); (*valVar) = val.Detach(); (*nReturned) = num.Detach(); } return hr;
}
STDMETHODIMP CVSet::getItems( /* [in] */BSTR basepoint, // in - if specified, only children of this node will be enumerated
/* [in] */BSTR startAfter, // in - the enumeration will begin with the next item in the map following this key.
/* [in] */BOOL bRecursive, // in - TRUE includes all sub-items, FALSE enumerates one level only.
/* [in] */ULONG bSize, // in - max number of elements to return (the size of the arrays)
/* [out] */SAFEARRAY ** keys, // out- array of keys
/* [out] */SAFEARRAY ** values, // out- array of values
/* [out] */LONG * nReturned // out- number of items copied
) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::getItems"); HRESULT hr = S_OK;
(*nReturned) = 0; (*keys) = 0; (*values) = 0;
m_cs.Lock(); CVarData * map = m_data; CString base; CString start; CString seg;
// Find the map to enumerate
base = basepoint; if ( base.GetLength() > 0 ) { map = GetItem(base); } if ( ! map ) { // not found
(*nReturned) = 0; } else { // Build an array of variants to hold the keys
int offset = 0;
SAFEARRAYBOUND bound[1]; LONG n = 0; LONG size = bSize; bound[0].lLbound = 0; bound[0].cElements = size;
// Allocate SAFEARRAYs for the keys and values
(*keys) = SafeArrayCreate(VT_VARIANT, 1, bound); (*values) = SafeArrayCreate(VT_VARIANT, 1, bound); start = startAfter; if ( base.GetLength() && start.GetLength() ) { // ASSERT( that LEFT(start,LEN(base)) = base
//start = start.Right(start.GetLength() - base.GetLength() - 1);
} if ( base.IsEmpty() && start.IsEmpty() ) { if ( map->GetData() && map->GetData()->vt != VT_EMPTY ) { long index[1];
index[0] = 0; // add the root element to the results
if ( (*keys)->fFeatures & FADF_BSTR ) { SafeArrayPutElement((*keys),index,_T("")); } else { // VBScript can only use VARIANT arrays (see getItems2)
_variant_t tempKey; tempKey = _T(""); SafeArrayPutElement((*keys),index,&tempKey); } SafeArrayPutElement((*values),index,map->GetData()); offset++; } } if ( map->HasChildren() ) { BuildVariantKeyValueArray(base,start,map->GetChildren(),(*keys), (*values),&offset,bSize,bRecursive); } (*nReturned) = offset; } m_cs.Unlock();
return hr; }
// helper function for getItems. Fills SAFEARRAYs of keys and values
// if the varset is indexed, the items will be returned in sorted order, o.w. they will be in arbitrary (but consistent) order.
void CVSet::BuildVariantKeyValueArray( CString prefix, // in - string to tack on to the beginning of each key (used when enumerating subkeys)
CString startAfter, // in - optional, enumerates only those items that alphabetically follow this one.
CMapStringToVar * map, // in - map containing the data
SAFEARRAY * keys, // i/o- array that will contain the key values for the requested items
SAFEARRAY * pVars, // i/o- array that will contain the data values for the requested items
int * offset, // i/o- number of items copied to the arrays (index to use for next insertion)
int maxOffset, // in - allocated size of the arrays
BOOL bRecurse // in - whether to recursively process children
) { MC_LOGBLOCKIF(VARSET_LOGLEVEL_INTERNAL,"CVSet::BuildVariantKeyValueArray"); int i; int nItems; CVarData * pObj; CString key; // last segment of key name
POSITION pos; CComBSTR val; // fully qualified key name to add to array (val = prefix.key)
CComVariant var; // value to add to array
BOOL includeSomeChildrenOnly;
CDottedString dBase(prefix); CDottedString dStartItem(startAfter); int depth = dBase.NumSegments(); if ( ! map ) return; // no data => nothing to do
if ( (*offset) >= maxOffset ) return; // the arrays are full
includeSomeChildrenOnly = dStartItem.NumSegments() > depth;
nItems = map->GetCount(); // If we're not using an index, the items will be returned in arbitrary order
if ( ! m_Indexed ) { if ( includeSomeChildrenOnly && bRecurse ) { // the startAfter item is in a subtree. Find the appropriate element at this level and recursively continue the search
dStartItem.GetSegment(depth,key); if ( map->Lookup(key,pObj) ) { // found the object
if ( ! prefix.IsEmpty() ) { BuildVariantKeyValueArray(prefix+_T(".")+key,startAfter,pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse); } else { BuildVariantKeyValueArray(key,startAfter,pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse); } } // we've included the children of this item that come after 'startAfter',
// now process the rest of the items at this level
// make sure there's still room
if ( (*offset) >= maxOffset ) return; // the arrays are full
} // this is the usual case. process the items at this level, starting with the element following StartAfter.
// Get a pointer to that first element
if ( startAfter.GetLength() > prefix.GetLength()) { CString startItem; dStartItem.GetSegment(depth,startItem); // this returns the position before startItem
pos = (POSITION)map->GetPositionAt(startItem); if (!pos) return; map->GetNextAssoc(pos,key,pObj); } else { pos = map->GetStartPosition(); }
for ( i = 0 ; pos && i < nItems ; i++ ) { map->GetNextAssoc(pos,key,pObj); if ( ! prefix.IsEmpty() ) { val = prefix + L"." + key; } else { val = key; } // copy each item into the arrays
ASSERT((*offset) < maxOffset); var.Copy(pObj->GetData()); LONG index[1]; index[0] = (*offset); SafeArrayPutElement(pVars,index,&var); if ( keys->fFeatures & FADF_BSTR ) { SafeArrayPutElement(keys,index,val); } else { // VBScript can only use VARIANT arrays (see getItems2)
_variant_t tempKey; tempKey = val; SafeArrayPutElement(keys,index,&tempKey); }
var.Clear(); (*offset)++; if ( *offset >= maxOffset ) break; // arrays are full - stop
if ( bRecurse && pObj->HasChildren() ) { // Recursively do the sub-map
if ( ! prefix.IsEmpty() ) { BuildVariantKeyValueArray(prefix+L"."+key,"",pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse); } else { BuildVariantKeyValueArray(key,"",pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse); } if ( *offset >= maxOffset ) break; // arrays are full - stop
} } } else { // Use the index to enumerate the items in alphabetical order
CIndexItem * curr; CIndexTree * ndx = map->GetIndex(); ASSERT (ndx != NULL);
if ( includeSomeChildrenOnly && bRecurse ) { // the startAfter item is in a subtree. Find the appropriate element at this level and recursively continue the search
dStartItem.GetSegment(depth,key); if ( map->Lookup(key,pObj) ) { // found the object
if ( ! prefix.IsEmpty() ) { BuildVariantKeyValueArray(prefix+_T(".")+key,startAfter,pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse); } else { BuildVariantKeyValueArray(key,startAfter,pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse); } } // we've included the children of this item that come after 'startAfter',
// now process the rest of the items at this level
// make sure there's still room
if ( (*offset) >= maxOffset ) return; // the arrays are full
} // Get a pointer to the first item at this level AFTER startAfter
if ( startAfter.GetLength() > prefix.GetLength() ) { CString startItem; dStartItem.GetSegment(depth,startItem); // if a starting item is specified, try using the hash function to find it in the table
curr = map->GetIndexAt(startItem); if ( curr ) { curr = ndx->GetNextItem(curr); } else { // The startAfter item is not in the table. Search the tree to find
// the first item that would follow it if it were there
curr = ndx->GetFirstAfter(startItem); } } else { curr = ndx->GetFirstItem(); } // Process all the items
while ( curr ) { pObj = curr->GetValue(); key = curr->GetKey();
curr = ndx->GetNextItem(curr); if ( ! prefix.IsEmpty() ) { val = prefix + L"." + key; } else { val = key; } // add each item to the arrays
ASSERT((*offset) < maxOffset); var.Copy(pObj->GetData()); LONG index[1]; index[0] = (*offset); SafeArrayPutElement(pVars,index,&var); if ( keys->fFeatures & FADF_BSTR ) { SafeArrayPutElement(keys,index,val); } else { // VBScript can only use VARIANT arrays (see getItems2)
_variant_t tempKey; tempKey = val; SafeArrayPutElement(keys,index,&tempKey); }
var.Clear(); (*offset)++; if ( *offset >= maxOffset ) break; // arrays are full - stop
if ( bRecurse && pObj->HasChildren() ) { // Recursively do the sub-map
if ( ! prefix.IsEmpty() ) { BuildVariantKeyValueArray(prefix+L"."+key,"",pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse); } else { BuildVariantKeyValueArray(key,"",pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse); } if ( *offset >= maxOffset ) break; // arrays are full - stop
} } } }
STDMETHODIMP CVSet::Clear() { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::Clear"); HRESULT hr = S_OK; if ( m_Restrictions & VARSET_RESTRICT_NOCHANGEDATA ) { hr = E_ACCESSDENIED; } else { m_cs.Lock(); m_bNeedToSave = TRUE; m_data->RemoveAll(); m_data->GetData()->Clear(); m_data->GetData()->ChangeType(VT_EMPTY); m_nItems = 0; m_cs.Unlock(); } return hr; }
//////////////IPersistStreamInit//////////////////////////////////////////////////////
STDMETHODIMP CVSet::GetClassID(CLSID __RPC_FAR *pClassID) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) (*pClassID) = CLSID_VarSet; return S_OK; }
STDMETHODIMP CVSet::IsDirty() { AFX_MANAGE_STATE(AfxGetStaticModuleState()) if ( m_bNeedToSave ) { return S_OK; } else { return S_FALSE; } } STDMETHODIMP CVSet::Load(LPSTREAM pStm) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) ULONG result = 0; HRESULT hr;
m_cs.Lock();
do { // once
hr = pStm->Read(&m_nItems,(sizeof m_nItems),&result); if ( FAILED(hr) ) break; hr = pStm->Read(&m_CaseSensitive,(sizeof m_CaseSensitive),&result); if ( FAILED(hr) ) break; hr = pStm->Read(&m_Indexed,(sizeof m_Indexed),&result); if ( FAILED(hr) ) break; hr = m_data->ReadFromStream(pStm); m_bNeedToSave = FALSE; m_bLoaded = TRUE; } while (FALSE);
m_cs.Unlock();
return hr; } STDMETHODIMP CVSet::Save(LPSTREAM pStm,BOOL fClearDirty) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) ULONG result = 0; HRESULT hr;
m_cs.Lock();
do { // once
hr = pStm->Write(&m_nItems,(sizeof m_nItems),&result); if ( FAILED(hr) ) break; hr = pStm->Write(&m_CaseSensitive,(sizeof m_CaseSensitive),&result); if ( FAILED(hr) ) break; hr = pStm->Write(&m_Indexed,(sizeof m_Indexed),&result); if ( FAILED(hr) ) break; hr = m_data->WriteToStream(pStm); if ( fClearDirty ) { m_bNeedToSave = FALSE; } }while (FALSE);
m_cs.Unlock(); return hr; }
STDMETHODIMP CVSet::GetSizeMax(ULARGE_INTEGER __RPC_FAR *pCbSize) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_INTERNAL,"VarSet GetSizeMax");
HRESULT hr = S_OK;
if ( pCbSize == NULL ) { return E_POINTER; } else { LPSTREAM pStr = NULL; DWORD rc; STATSTG stats; DWORD requiredLength = 0;
rc = CreateStreamOnHGlobal(NULL,TRUE,&pStr); if ( ! rc ) { hr = Save(pStr,FALSE); if (SUCCEEDED(hr) ) { hr = pStr->Stat(&stats,STATFLAG_NONAME); if (SUCCEEDED(hr) ) { requiredLength = stats.cbSize.LowPart; } } pStr->Release(); }
pCbSize->LowPart = requiredLength; MC_LOG("Size is " << McString::makeStr(requiredLength) ); pCbSize->HighPart = 0; } return hr; } STDMETHODIMP CVSet::InitNew() { AFX_MANAGE_STATE(AfxGetStaticModuleState()) if ( m_bLoaded ) { return E_UNEXPECTED; } else { m_cs.Lock(); InitProperties(); m_cs.Unlock(); return S_OK; } }
STDMETHODIMP CVSet::ImportSubTree(/*[in] */ BSTR key, /* [in] */ IVarSet * pVarSet) {
AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::ImportSubTree");
HRESULT hr = S_OK; VARIANT value; ULONG nGot; _bstr_t keyB; _bstr_t newkey; // make sure the varset is valid
// enumerate the varset, inserting each item into the tree as key.item
IEnumVARIANT * varEnum = NULL; IUnknown * pUnk = NULL;
// TODO: need to speed this up by using getItems
hr = pVarSet->get__NewEnum(&pUnk); if ( SUCCEEDED(hr) ) { hr = pUnk->QueryInterface(IID_IEnumVARIANT,(void**)&varEnum); pUnk->Release(); } if ( SUCCEEDED(hr)) { value.vt = VT_EMPTY; while ( SUCCEEDED(hr = varEnum->Next(1,&value,&nGot)) ) { if ( nGot==1 ) { keyB = value.bstrVal; newkey = key; if ( newkey.length() ) { newkey += _T("."); } newkey += keyB; hr = pVarSet->get(keyB,&value); if ( SUCCEEDED(hr ) ) { hr = put(newkey,value); } } else { break; } if ( FAILED(hr) ) break; } if ( varEnum ) varEnum->Release(); } varEnum = NULL; // clean up
return hr; }
STDMETHODIMP CVSet::getReference( /* [in] */ BSTR key, /* [out,retval] */IVarSet ** ppVarSet) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::getReference");
HRESULT hr = S_OK; CVarData * item = GetItem(key); typedef CComObject<CVSet> myvset;
myvset * pVarSet;
if ( ! ppVarSet ) { hr = E_POINTER; } else { if ( item ) { pVarSet = new myvset; AddRef(); ((CVSet*)pVarSet)->SetData(this,item,m_Restrictions); hr = pVarSet->QueryInterface(IID_IVarSet,(void**)ppVarSet); } else { hr = TYPE_E_ELEMENTNOTFOUND; } } return hr; }
STDMETHODIMP CVSet::DumpToFile(BSTR filename) { AFX_MANAGE_STATE(AfxGetStaticModuleState())
HRESULT hr = S_OK;
#ifdef STRIPPED_VARSET
USES_CONVERSION; TError errLog; errLog.LogOpen((WCHAR*)filename,1,0);
errLog.MsgWrite(0,L"VarSet"); errLog.MsgWrite(0,L"Case Sensitive: %s, Indexed: %s",(m_CaseSensitive ? L"Yes" : L"No"),(m_Indexed ? L"Yes" : L"No") ); errLog.MsgWrite(0,L"User Data ( %ld ) items",m_nItems); #else
#endif
m_cs.Lock(); CVarData * map = m_data; CString start; CString seg; // Build an array of variants to hold the keys
CComVariant * pVars = new CComVariant[m_data->CountItems()+1]; CString key; int offset = 1; key = _T(""); if (!pVars) { m_cs.Unlock(); return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); }
// include the root item in the list
pVars[0] = key; if ( map->HasChildren() ) { BuildVariantKeyArray(key,map->GetChildren(),pVars,&offset); } m_cs.Unlock();
if ( ! m_Indexed ) { // Sort the results
qsort(pVars,offset,(sizeof CComVariant),&SortComVariantStrings); }
for ( int i = 0 ; i < offset ; i++ ) { CVarData * data; CString value; CString key;
key = pVars[i].bstrVal;
data = GetItem(key);
if ( data ) { switch ( data->GetData()->vt ) { case VT_EMPTY: value = _T("<Empty>"); break; case VT_NULL: value = _T("<Null>"); break; case VT_I2: value.Format(_T("%ld"),data->GetData()->iVal); break; case VT_I4: value.Format(_T("%ld"),data->GetData()->lVal); break; case VT_BSTR: value = data->GetData()->bstrVal; break; default: value.Format(_T("variant type=0x%lx"),data->GetData()->vt); break; } #ifdef STRIPPED_VARSET
errLog.MsgWrite(0,L" [%ls] %ls",key.GetBuffer(0),value.GetBuffer(0)); #else
#endif
} else { #ifdef STRIPPED_VARSET
errLog.MsgWrite(0,L" [%ls] <No Value>",key.GetBuffer(0)); #else
#endif
} } delete [] pVars; return hr; }
STDMETHODIMP CVSet::get_Indexed(BOOL *pVal) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get_Indexed"); if ( pVal == NULL ) return E_POINTER; m_cs.Lock(); (*pVal) = m_Indexed; m_cs.Unlock();
return S_OK; }
STDMETHODIMP CVSet::put_Indexed(BOOL newVal) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::put_Indexed"); HRESULT hr = S_OK; if ( m_Restrictions & VARSET_RESTRICT_NOCHANGEPROPS ) { hr = E_ACCESSDENIED; } else { m_cs.Lock(); m_bNeedToSave = TRUE; m_Indexed = newVal; m_data->SetIndexed(m_Indexed); m_cs.Unlock(); } return hr; }
STDMETHODIMP CVSet::get_AllowRehashing(BOOL *pVal) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get_AllowRehashing"); if ( pVal == NULL ) return E_POINTER; m_cs.Lock(); (*pVal) = m_AllowRehashing; m_cs.Unlock();
return S_OK; }
STDMETHODIMP CVSet::put_AllowRehashing(BOOL newVal) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::put_AllowRehashing"); HRESULT hr = S_OK; if ( m_Restrictions & VARSET_RESTRICT_NOCHANGEPROPS ) { hr = E_ACCESSDENIED; } else {
m_cs.Lock(); m_bNeedToSave = TRUE; m_AllowRehashing = newVal; m_data->SetAllowRehashing(newVal);
m_cs.Unlock(); } return hr; }
STDMETHODIMP CVSet::get_Restrictions(DWORD * restrictions) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) HRESULT hr = S_OK;
if ( restrictions == NULL ) { hr = E_POINTER; } else { (*restrictions) = m_Restrictions; } return hr; }
STDMETHODIMP CVSet::put_Restrictions(DWORD newRestrictions) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) HRESULT hr = S_OK; DWORD rAdding = newRestrictions & ~m_Restrictions; DWORD rRemoving = ~newRestrictions & m_Restrictions;
// Can't remove any restrictions passed down from parent.
if ( ( rRemoving & m_ImmutableRestrictions) ) { hr = E_ACCESSDENIED; } else if ( rAdding & ! VARSET_RESTRICT_ALL ) { hr = E_NOTIMPL; } else { // the change is OK.
m_Restrictions = newRestrictions; } return hr; }
// IMarshal implemention
// This marshals the varset to a stream that is then sent across the wire
STDMETHODIMP CVSet::GetUnmarshalClass(REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) *pCid = GetObjectCLSID(); return S_OK; } STDMETHODIMP CVSet::GetMarshalSizeMax(REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, DWORD *pSize) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) HRESULT hr = S_OK; ULARGE_INTEGER uli; hr = GetSizeMax(&uli);
if (SUCCEEDED(hr)) { *pSize = uli.LowPart; } return hr; } STDMETHODIMP CVSet::MarshalInterface(IStream *pStm, REFIID riid, void *pv, DWORD dwDestContext, void *pvDestCtx, DWORD mshlflags) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) HRESULT hr = S_OK; // Save the varset's data to a stream
hr = Save(pStm, FALSE); return hr; } STDMETHODIMP CVSet::UnmarshalInterface(IStream *pStm, REFIID riid, void **ppv) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) HRESULT hr = S_OK; // Load up the data from the stream using our IPersistStream implementation
hr = Load(pStm);
if ( SUCCEEDED(hr) ) { hr = QueryInterface(riid,ppv); } return hr; } STDMETHODIMP CVSet::ReleaseMarshalData(IStream * /*pStmNotNeeded*/) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) // we don't keep any state data, so there's nothing to release
// since we just read the object from the stream, the stream's pointer should already be at the end,
// so there's nothing left for us to do here
return S_OK; } STDMETHODIMP CVSet::DisconnectObject(DWORD /*dwReservedNotUsed*/) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) return S_OK; }
|