
//+----------------------------------------------------------------------------
//
//	File:
//		dacache.cpp
//
//	Contents:
//		implementation of the data advise cache - CDataAdviseCache
//
//	Classes:
//		CDataAdviseCache
//
//	Functions:
//
//	History:
//		24-Jan-94 alexgo    first pass at converting to Cairo-style
//				    memory allocation
//		01/11/94 - AlexGo  - added VDATEHEAP macros to every function
//			and method
//		11/02/93 - ChrisWe - file inspection and cleanup
//		12/15/92 - JasonFul - Created
//
//-----------------------------------------------------------------------------

#include <le2int.h>

#pragma SEG(dacache)

#include <dacache.h>
#include <reterr.h>

ASSERTDATA


//+----------------------------------------------------------------------------
//
//	Member:
//		CDataAdviseCache::CreateDataAdviseCache, static public
//
//	Synopsis:
//		Creates an instance of the CDataAdviseCache
//
//	Arguments:
//		[pp] -- pointer to a location to where to return the
//			newly created CDataAdviseCache
//
//	Returns:
//		E_OUTOFMEMORY, S_OK
//
//	Notes:
//
//	History:
//		11/02/93 - ChrisWe - file cleanup and inspection
//
//-----------------------------------------------------------------------------

#pragma SEG(CreateDataAdviseCache)
FARINTERNAL CDataAdviseCache::CreateDataAdviseCache(LPDATAADVCACHE FAR* pp)
{
	VDATEHEAP();

	VDATEPTRIN(pp, LPDATAADVCACHE);

	// try to allocate the CDataAdviseCache
	if(NULL == (*pp = new DATAADVCACHE))
		return ReportResult(0, E_OUTOFMEMORY, 0, 0);

	// initialize the DataAdviseHolder member
	if(CreateDataAdviseHolder(&((*pp)->m_pDAH)) != NOERROR)
	{
		// free the DataAdviseCache
		delete *pp;
		*pp = NULL;

		return ReportResult(0, E_OUTOFMEMORY, 0, 0);
	}

	return(NOERROR);
}


//+----------------------------------------------------------------------------
//
//	Member:
//		CDataAdviseCache::CDataAdviseCache, private
//
//	Synopsis:
//		constructor
//
//	Arguments:
//		none
//
//	Notes:
//		This is private because it does not create a fully
//		formed CDataAdviseCache.  m_pDAH must be allocated before
//		this can be used.  That is done by the static member
//		CreateDataAdviseCache, which first calls this
//
//	History:
//		11/02/93 - ChrisWe - file cleanup and inspection
//
//-----------------------------------------------------------------------------

#pragma SEG(CDataAdviseCache_ctor)
CDataAdviseCache::CDataAdviseCache():
	m_mapClientToDelegate(MEMCTX_TASK)
{
	VDATEHEAP();

	//now allocated with system allocator
	//Assert(CoMemctxOf(this) == MEMCTX_TASK);

	// no data advise holder allocated yet
	m_pDAH = NULL;
}


//+----------------------------------------------------------------------------
//
//	Member:
//		CDataAdviseCache::~CDataAdviseCache, public
//
//	Synopsis:
//		destructor
//
//	Arguments:
//		none
//
//	Requires:
//		successful call to CreateDataAdviseCache
//
//	Notes:
//
//	History:
//		11/02/93 - ChrisWe - file cleanup and inspection
//
//-----------------------------------------------------------------------------

#pragma SEG(CDataAdviseCache_dtor)
CDataAdviseCache::~CDataAdviseCache()
{
	VDATEHEAP();

	// release the data advise holder
	if( m_pDAH )
	{
		m_pDAH->Release();
	}
}


//+----------------------------------------------------------------------------
//
//	Member:
//		CDataAdviseCache::Advise, public
//
//	Synopsis:
//		Records an advise sink for later use.  The sink will be
//		registered with the data object, if there is one, and
//		will be remembered for later registration with the data object,
//		in case it should go away, and return later.
//
//	Effects:
//
//	Arguments:
//		[pDataObject] -- the data object that the advise sink is
//			interested in changes to; may be null if the
//			data object isn't running
//		[pFetc] -- the format the advise sink would like to recieve
//			new data in
//		[advf] -- advise control flags ADVF_*
//		[pAdvise] -- the advise sink
//		[pdwClient] -- a token identifying the connection
//
//	Returns:
//		E_OUTOFMEMORY, S_OK
//
//	Notes:
//
//	History:
//		11/02/93 - ChrisWe - file cleanup and inspection
//
//-----------------------------------------------------------------------------

#pragma SEG(CDataAdviseCache_Advise)
HRESULT CDataAdviseCache::Advise(LPDATAOBJECT pDataObject,
		FORMATETC FAR* pFetc, DWORD advf, LPADVISESINK pAdvise,
		DWORD FAR* pdwClient)
		// first 4 parms are as in DataObject::Advise
{
	VDATEHEAP();

	DWORD dwDelegate = 0; // the delegate connection number
	HRESULT hr;

	// if there is a data object, ask to be advised of changes
	if(pDataObject != NULL)
		RetErr(pDataObject->DAdvise(pFetc, advf, pAdvise, &dwDelegate));

	// if there is no data object, (i.e. the object is not active,
	// dwDelegate is zero

	// Here we are using the data advise holder only to hold advise
	// connections. We are not going to use it to send OnDataChange to
	// sinks.
	
	// REVIEW, handling of ADVF_ONLYONCE seems broken...
	// it's clear that we can't cope with this flag properly;  we have
	// no way of knowing when the notification takes place, and therefore
	// we can't remove the entry from m_pDAH.  The notification may have
	// taken place above, and it may not have.  If the data object wasn't
	// around, then the advise request here is lost, and the sink will
	// never be notified.  Or, if the request isn't PRIMEFIRST, and the
	// data object is deactivated, then the data object loses the request,
	// and on subsequent activation, we won't readvise it on EnumAndAdvise.
	// So, what good are we for ONLYONCE sinks?  What does this break?
	if(advf & ADVF_ONLYONCE)
		return 	NOERROR;

	// keep a local copy of the advise
	hr = m_pDAH->Advise(NULL, pFetc, advf, pAdvise, pdwClient);

	// if we failed to keep a local reference to the advise sink,
	// we won't be able to maintain this mapping, so remove the
	// advise on the data object, if there is one
	if (hr != NOERROR)
	{
	Exit1:
		if (pDataObject != NULL)
			pDataObject->DUnadvise(dwDelegate);

		return(hr);
	}

	// create a map entry from *pdwClient -> dwDelegate

	// if the map entry creation failed, undo all work
	if (m_mapClientToDelegate.SetAt(*pdwClient, dwDelegate) != TRUE)
	{
		// map failed to allocate memory, undo advise since we won't
		// be able to find this one again
		m_pDAH->Unadvise(*pdwClient);

		// map entry creation must have failed from lack of allocation
		hr = ReportResult(0, E_OUTOFMEMORY, 0, 0);

		// undo the advise on the data object
		goto Exit1;
	}

	return(NOERROR);
}


//+----------------------------------------------------------------------------
//
//	Member:
//		CDataAdviseCache::Unadvise, public
//
//	Synopsis:
//		Remove an advise sink from the list of sinks the advise cache
//		maintains; the sink is also removed from the list of items
//		registered with the data object, if the data object is provided
//
//	Effects:
//
//	Arguments:
//		[pDataObject] -- the data object, if it is running, or NULL
//		[dwClient] -- the token that identifies this connection
//
//	Returns:
//		OLE_E_NOCONNECTION, for a bad dwClient
//		S_OK
//
//	Notes:
//
//	History:
//		11/02/93 - ChrisWe - file cleanup and inspection
//
//-----------------------------------------------------------------------------

#pragma SEG(CDataAdviseCache_Unadvise)
HRESULT CDataAdviseCache::Unadvise(IDataObject FAR* pDataObject, DWORD dwClient)
{
	VDATEHEAP();

	DWORD dwDelegate;

	// retrieve dwDelegate before removing from map
	if(pDataObject != NULL)
		RetErr(ClientToDelegate(dwClient, &dwDelegate));

	// do these first so error from remote unadvise is last(which might
	// be sync call during async dispatch

	RetErr(m_pDAH->Unadvise(dwClient));

	// If the above line succeeded, Remove Key must succeed.
	Verify(TRUE == m_mapClientToDelegate.RemoveKey(dwClient));

	// Delegate connection could be 0 if it did not accept the Advise
	if(pDataObject != NULL && dwDelegate != 0)
	{
		// Unadvise is asynchronous, don't worry about return value
		pDataObject->DUnadvise(dwDelegate);
	}
	
	return NOERROR;
}


//+----------------------------------------------------------------------------
//
//	Member:
//		CDataAdviseCache::EnumAdvise, public
//
//	Synopsis:
//		returns an enumerator over the advisory connections
//
//	Arguments:
//		[ppenumAdvise] -- pointer to where to return the enumerator
//
//	Returns:
//		E_OUTOFMEMORY, S_OK
//
//	Notes:
//
//	History:
//		11/02/93 - ChrisWe - file cleanup and inspection
//
//-----------------------------------------------------------------------------

#pragma SEG(CDataAdviseCache_EnumAdvise)
HRESULT CDataAdviseCache::EnumAdvise(LPENUMSTATDATA FAR* ppenumAdvise)
{
	VDATEHEAP();

	return m_pDAH->EnumAdvise(ppenumAdvise);
}


//+----------------------------------------------------------------------------
//
//	Member:
//		CDataAdviseCache::ClientToDelegate, private
//
//	Synopsis:
//		returns the delegate connection id for a given client
//		connection id
//
//	Arguments:
//		[dwClient] -- the client connection identifier
//		[pdwDelegate] -- pointer to where to return the delegate
//			connection identifier
//
//	Returns:
//		OLE_E_NOCONNECTION, for a bad dwClient
//		S_OK
//
//	Notes:
//
//	History:
//		11/02/93 - ChrisWe - file cleanup and inspection
//
//-----------------------------------------------------------------------------

#pragma SEG(CDataAdviseCache_ClientToDelegate)
HRESULT CDataAdviseCache::ClientToDelegate(DWORD dwClient,
		DWORD FAR* pdwDelegate)
{
	VDATEHEAP();

	VDATEPTRIN(pdwDelegate, DWORD);
	DWORD dwDelegate = *pdwDelegate = 0;

	if (FALSE == m_mapClientToDelegate.Lookup(dwClient, dwDelegate))
		return(ReportResult(0, OLE_E_NOCONNECTION, 0, 0));

	*pdwDelegate = dwDelegate;
	return NOERROR;
}


//+----------------------------------------------------------------------------
//
//	Member:
//		CDataAdviseCache::EnumAndAdvise, public
//
//	Synopsis:
//		Enumerate all the advise sinks registered in the data advise
//		cache.  For each one, either register it with the
//		given data object, or deregister it, depending on [fAdvise].
//		Does not change what sinks are known to the data advise cache.
//
//	Effects:
//
//	Arguments:
//		[pDataDelegate] -- a data object that the advise sinks
//			are interested in
//		[fAdvise] -- if TRUE, register the advise sinks with
//			pDataDelegate object (with IDataObject::DAdvise();) if
//			FALSE, the deregister the advise sinks
//			(with DUnadvise().)
//
//	Returns:
//		OLE_E_NOCONNECTION, if the mapping is corrupt  (REVIEW!)
//		S_OK
//
//	Notes:
//
//	History:
//		11/04/93 - ChrisWe - file cleanup and inspection
//
//-----------------------------------------------------------------------------

#pragma SEG(CDataAdviseCache_EnumAndAdvise)
HRESULT CDataAdviseCache::EnumAndAdvise(LPDATAOBJECT pDataDelegate,
		BOOL fAdvise)
{
	VDATEHEAP();

	VDATEIFACE(pDataDelegate);
	LPENUMSTATDATA penumAdvise; // enumerator for the data advise holder
	DWORD dwDelegate; // delegate connection id for the current connection
	STATDATA statdata; // filled in by the penumAdvise enumerator
	HRESULT hresult = NOERROR; // current error status

	// get an enumerator from the data advise holder
	RetErr(m_pDAH->EnumAdvise(&penumAdvise));

	// repeat for each advise sink in the data advise holder...
	while(NOERROR == penumAdvise->Next(1, &statdata, NULL))
	{
		if(fAdvise)
		{
			// It is possible that the delegate's Advise will fail
			// even though we allowed the advise on the loaded
			// object to succeed(because the delegate is "pickier".)
			if(NOERROR==pDataDelegate->DAdvise(&statdata.formatetc,
					statdata.advf, statdata.pAdvSink,
					&dwDelegate))
			{
				// we know the key is present; this SetAt
				// should not fail
				Verify(m_mapClientToDelegate.SetAt(
						statdata.dwConnection,
						dwDelegate));
			}
		}
		else // unadvise
		{
			if((hresult=ClientToDelegate(statdata.dwConnection,
					&dwDelegate)) != NOERROR)
			{
				AssertSz(0, "Corrupt mapping");
				UtReleaseStatData(&statdata);
				goto errRtn;
			}
				
			if(dwDelegate != 0)
			{
				if(NOERROR==pDataDelegate->DUnadvise(
						dwDelegate))
				{
					// we know the key is present; this
					// SetAt should not fail
					Verify(m_mapClientToDelegate.SetAt(
							statdata.dwConnection,
							0));
				}
				else
				{
					Warn("Delegate didn't accept unadvise");
				}
			}
		}
		UtReleaseStatData(&statdata);
	}

  errRtn:

	// release the enumerator
	penumAdvise->Release();
	return hresult;
}

