loggingservices/eventlogger/LogServ/src/LogServView.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 22 Jan 2010 11:06:30 +0200
changeset 0 08ec8eefde2f
child 55 44f437012c90
permissions -rw-r--r--
Revision: 201003 Kit: 201003

// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//
#include "LogServView.h"
#include "logpackage.h"
#include "logservpanic.h"
#include "LogServBackupInterface.h"
#include "LogServViewChangeManager.h"
#include "LogServDatabaseChangeInterface.h"
#include "LogServCacheTypes.h"
#include "LogServSqlStrings.h"
#include "LOGFILTQ.H"

// Constants
const TInt KLogViewContentsGranuality = 20;
const TInt KLogViewLockStatusEventGranularity = 3;

TDbColNo CLogServViewBase::iIdColNo = 0;
TDbColNo CLogServViewBase::iTypeColNo = 0;
TDbColNo CLogServViewBase::iFlagColNo[] = {0, 0, 0, 0};

TDbColNo CLogServViewRecent::iIdColNo = 0;
TDbColNo CLogServViewRecent::iRecentColNo = 0;
TDbColNo CLogServViewRecent::iDuplicateColNo = 0;

//diagnostic message for the platform security
const char* KIgnoreDiagnostic = "This diagnostic message does not indicate an error, please ignore it";

/////////////////////////////////////////////////////////////////////////////////////////
// -----> CLogServViewBase (source)
/////////////////////////////////////////////////////////////////////////////////////////
CLogServViewBase::CLogServViewBase(MLogServDatabaseTransactionInterface& aDatabase, 
                                   MLogServBackupInterface& aBackupInterface, 
                                   CLogPackage& aPackage, TLogViewType aType, TLogViewId aViewId, 
                                   const RMessage2& aMessage) :
    iDatabase(aDatabase), 
    iPackage(aPackage),
    iBackupInterface(aBackupInterface), 
    iType(aType), 
    iViewId(aViewId), 
    iMessage (aMessage)
	{
	}

CLogServViewBase::~CLogServViewBase()
	{
	iDatabase.DTIChangeInterface().DCIRequestChangeNotificationsCancel(*this);
	iBackupInterface.BIObserverRemove(*this);
	//
	iViewContents.Close();
	//	
	iStandardTypeSecurityCache.Close();
	//
	delete iLockChangeObserver;
	delete iSql;
	delete iChangeManager;
	}


void CLogServViewBase::ConstructL()
	{
	// Handles changes for this view
	iChangeManager = CLogServViewChangeManager::NewL(iDatabase.DTIChangeInterface());

	// Register for change events
	iDatabase.DTIChangeInterface().DCIRequestChangeNotificationsL(*this);

	// Register for backup events
	iBackupInterface.BIObserverAddL(*this, MLogServBackupInterface::EObjectView);

	// Observes when the view is locked/unlocked due to a backup
	iLockChangeObserver = CLogServViewLockObserver::NewL(iBackupInterface);

	const RArray<TUid>& arrTUids = iDatabase.DTIUidsOfStandardTypes();
	TInt count = arrTUids.Count();
	iStandardTypeSecurityCache.ReserveL(count);
	for(TInt i=0; i < count; i++)
		{
		SStandardTypeSecurity securitySetting;
		securitySetting.eventType = arrTUids[i];
		securitySetting.readAccess = iDatabase.DTIIsAllowed(EReadOp, iMessage, arrTUids[i], KIgnoreDiagnostic);
		securitySetting.writeAccess = iDatabase.DTIIsAllowed(EWriteOp, iMessage, arrTUids[i], KIgnoreDiagnostic);
		TInt err = iStandardTypeSecurityCache.Append(securitySetting);	
        __ASSERT_ALWAYS(err == KErrNone, Panic(ELogArrayReserved));
		}
	}

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

void CLogServViewBase::DCOHandleChangeEventsL(const CLogChangeDefinition& aChanges)
	{
	// Just return if the view isn't setup
	if	(!iSql)
		return;

	TRAPD(error, DoHandleChangeEventsL(aChanges));
	if	(error != KErrNone)
		{
		iViewContents.Close();
	    iViewContentsReady = EFalse;
		iRebuildViewContents = ETrue;
		}
	}

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

void CLogServViewBase::BOHandleEventL(TLogServBackupEvent aEvent)
	{
	switch(aEvent)
		{
	case EBackupStarting:
		iViewContents.Close();
	    iViewContentsReady = EFalse;
		break;
	case EBackupEnded:
		RebuildViewL();
		break;
		}
	}

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

#pragma BullseyeCoverage off

/**
Remove an entry from the view.
By default you can't remove an event from a view.
*/
void CLogServViewBase::RemoveL(const RMessage2& /*aMessage*/)
	{
	User::Leave(KErrNotSupported);
	}

/**
By default you can't clear duplicates from a view
*/
void CLogServViewBase::ClearDuplicatesL(const RMessage2& /*aMessage*/)
	{
	User::Leave(KErrNotSupported);
	}

#pragma BullseyeCoverage on

/**
Set the flags of all the entries in the view.
*/
void CLogServViewBase::SetFlagsL(const RMessage2& aMessage)
	{
	if	(ViewIsReady())
		{
		// Flags from client
		const TLogFlags flags = static_cast<TLogFlags>(aMessage.Int2());
		RLogDbView view;
		view.PrepareLC(iDatabase.DTIDatabase(), *iSql);
		InitializeColumnsL(view);
		if(view.FirstL())
			{
            iDatabase.DTIBeginWithRollBackProtectionLC();
			// Iterate through the events
			do
				{
				// Get current event id
				view.GetL();
				const TLogId id = view.ColInt32(CLogServViewBase::iIdColNo);
				TUint8 eventTypeIndex = view.ColUint8(CLogServViewBase::iTypeColNo);
				if(IsAllowed(EWriteOp, eventTypeIndex))
					{
					// Make the change
					view.UpdateL();
		            TInt bit = KLogFlagsCount;
		            while(bit--)
		                {
                        const TBool flagIsSet = flags & (0x1 << bit) ? 1 : 0;
		                view.SetColL(CLogServViewBase::iFlagColNo[bit], flagIsSet);
		                }
					view.PutL();
					iDatabase.DTIChangeInterface().DCISubmitChangedEventContextL(ELogChangeTypeEventChanged, id);
					}
				}
			while(view.NextL());
			iDatabase.DTICommitAndCancelRollbackProtectionL();
			}
		CleanupStack::PopAndDestroy(&view);
		}
	else
		::PanicClientL(aMessage, ELogViewRecentViewNotYetReadyForFlagSetting);
	}

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

/**
The client has requested change notifications
*/
void CLogServViewBase::RequestChangeNotifications(const RMessage2& aMessage)
	{
	iChangeManager->RequestChangeNotifications(aMessage);
	}

/**
The client has cancelled a previous change notification request
*/
void CLogServViewBase::RequestChangeNotificationsCancel()
	{
	iChangeManager->RequestChangeNotificationsCancel();
	}

/**
The client has requested lock status change notifications
*/
void CLogServViewBase::RequestLockStatusChanges(const RMessage2& aMessage)
	{
	iLockChangeObserver->RequestLockStatusChanges(aMessage);
	}

/**
The client has cancelled a previous change notification request
*/
void CLogServViewBase::RequestLockStatusChangesCancel()
	{
	iLockChangeObserver->RequestLockStatusChangesCancel();
	}

/**
The client has requested the current list of changes
*/
void CLogServViewBase::RequestChangesL(const RMessage2& aMessage)
	{
	if	(!iSql)
		::PanicClientL(aMessage, ELogViewNotSetupForChangesFetch);
	else
		iChangeManager->DeliverChangesL(aMessage);
	}

/**
Set up the server-side view based upon the client-side filter. This
method essentially fetches a client-side filter and then dynamically
executes the associated SELECT statement on the database to build up
a view. This view is then cached in terms of the entry ids (iViewContents).
*/
void CLogServViewBase::SetupL(const RMessage2& aMessage, TLogFilterConstructionType aFilterType)
	{
	// Get the query string
	const TPtrC pSQL(GetQueryStringL(aMessage, aFilterType));
	HBufC* buf = pSQL.AllocLC();
	// Setup the contents
	PrepareViewContentsL(pSQL);
	// Tidy up
	CleanupStack::Pop(buf);
	delete iSql;
	iSql = buf;
	}

/**
Returns the number of entries in this view
*/
TInt CLogServViewBase::Count() const
	{
	return const_cast<CLogServViewBase*>(this)->RebuildViewContentsIfNecessary() ? 0 : iViewContents.Count(); 
	}

/**
Get the log id at the specified index
*/
TLogId CLogServViewBase::At(TInt aIndex) const
	{
	return (TUint)aIndex < iViewContents.Count() ? iViewContents[aIndex] : KLogNullId;   
	}

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

void CLogServViewBase::DestroyList(TAny *aPtr)
	{
	CLogFilterList* filter = reinterpret_cast<CLogFilterList*>(aPtr);
	filter->ResetAndDestroy();
	delete filter;
	}

void CLogServViewBase::InitializeColumnsL(RDbRowSet& aRowSet)
    {
    if(CLogServViewBase::iIdColNo == 0)
        {
        CDbColSet* colset = aRowSet.ColSetL();
        CLogServViewBase::iIdColNo = colset->ColNo(KLogFieldIdString);
        CLogServViewBase::iTypeColNo = colset->ColNo(KLogFieldEventTypeString);
        for(TInt i=0;i<KLogFlagsCount;++i)
            {
            TDbColName colname;
            colname.Format(KLogFieldEventFlagString, i + 1);
            CLogServViewBase::iFlagColNo[i] = colset->ColNo(colname);
            __ASSERT_DEBUG(CLogServViewBase::iFlagColNo[i] > 0, User::Invariant());
            }
        delete colset;
        }
    __ASSERT_DEBUG(CLogServViewBase::iIdColNo > 0 && CLogServViewBase::iTypeColNo > 0, User::Invariant());  
    }

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

void CLogServViewBase::ResetViewContentsL(RDbRowSet& aRowSet)
	{
	// Get the view contents
	RArray<TLogId> viewContents(KLogViewContentsGranuality);
	CleanupClosePushL(viewContents);
	viewContents.ReserveL(aRowSet.CountL());
	if(aRowSet.FirstL())
		{
		do
			{
			// Get the id at the current position
			aRowSet.GetL();
			const TLogId id = aRowSet.ColInt32(CLogServViewBase::iIdColNo);
			TUint8 eventTypeIndex = aRowSet.ColUint8(CLogServViewBase::iTypeColNo);
			if(IsAllowed(EReadOp, eventTypeIndex))
			    {
				TInt err = viewContents.Append(id);
		        __ASSERT_ALWAYS(err == KErrNone, Panic(ELogArrayReserved));
			    }
			}
		while(aRowSet.NextL());
		}
	// Tidy up - don't leave from below here
    CleanupStack::Pop(&viewContents);
	iViewContents.Close();
	iViewContents = viewContents;
    iViewContentsReady = ETrue;
	}

void CLogServViewBase::PrepareViewContentsL(const TDesC& aSQL)
	{
	// Generate the view
    RLogDbView view;
    view.PrepareLC(iDatabase.DTIDatabase(), aSQL);
    InitializeColumnsL(view);
	// Reset the view
	ResetViewContentsL(view);
	CleanupStack::PopAndDestroy(&view);
	}

void CLogServViewBase::RebuildViewL()
	{
	if	(iSql && iSql->Length())
		PrepareViewContentsL(*iSql);
	}

//
//  The last change notification indication was not handled correctly.
//  Attempt to re-initialise the view
//
TInt CLogServViewBase::RebuildViewContentsIfNecessary()
	{
	TInt error = KErrNone;
	//
	if	(iRebuildViewContents && iSql && iSql->Length())
		{
		TRAP(error, 

			RebuildViewL();

			// The view is okay now
			iRebuildViewContents = EFalse;
			);
		}
	return error;
	}

void CLogServViewBase::DoHandleChangeEventsL(const CLogChangeDefinition& aChanges)
	{
    if(!ViewIsReady())
        {
        return;
        }
	
	// Generate the view
    RLogDbView view;
    view.PrepareLC(iDatabase.DTIDatabase(), *iSql);
    InitializeColumnsL(view);
	_LIT(KLogIdQuery, "Id = %d");
	TBuf<15> find;
	TInt changeIndex;

	// Prepare for a change transaction
	iChangeManager->ChangeTransactionPrepare();

	const TInt count = aChanges.Count();
	for(TInt i=0; i<count; i++)
		{
		// Fetch the change details
		TLogId logId = KLogNullId;
		TLogDatabaseChangeType type = aChanges.At(i, logId);

		// Mark the insertion position as 'not found'
		changeIndex = KErrNotFound;

		// Format the find query
		find.Format(KLogIdQuery, logId);

		// Handle the various change descriptions
		switch(type)
			{
		case ELogChangeTypeEventAdded:
			{
			__ASSERT_DEBUG(iViewContents.Find(logId) == KErrNotFound, Panic(ELogEventAlreadyInView));

			// See if the event is in the view
			if(view.FirstL())
				{
				const TDbQuery dbQuery(find);
				changeIndex = view.FindL(RDbRowSet::EForwards, dbQuery);
				if	(changeIndex >= 0)
					{
					view.GetL();
					TUint8 eventTypeIndex = view.ColUint8(CLogServViewBase::iTypeColNo);
					if (IsAllowed(EReadOp, eventTypeIndex))
						User::LeaveIfError(iViewContents.Insert(logId, changeIndex));
					
					iChangeManager->ChangeTransactionSubmitL(logId, ELogChangeTypeEventAdded, changeIndex);
					}
				}
			}
			break;
		case ELogChangeTypeEventChanged:
		case ELogChangeTypeEventChangedHidden:
			{
			// See if the event is in the view
			if	(view.FirstL() && (changeIndex = view.FindL(RDbRowSet::EForwards, TDbQuery(find))) >= KErrNone)
				{
				// If the event was already in the view then it has changed otherwise it's been added
				const TInt findIndex = iViewContents.Find(logId);
				if	(findIndex >= KErrNone)
					{
					// If the item that changed also caused its position within the view to be altered
					// then we need to simulate a delete, followed by an addition
					if	(findIndex != changeIndex)
						{
						iChangeManager->ChangeTransactionSubmitL(logId, ELogChangeTypeEventDeleted, findIndex);
						iChangeManager->ChangeTransactionSubmitL(logId, ELogChangeTypeEventAdded, changeIndex);
						}
					// Only tell the view if the CLogEvent has changed
					else if (type != ELogChangeTypeEventChangedHidden)
						{
						iChangeManager->ChangeTransactionSubmitL(logId, ELogChangeTypeEventChanged, changeIndex);
						}
					}
				else
					{
					User::LeaveIfError(iViewContents.Insert(logId, changeIndex));
					iChangeManager->ChangeTransactionSubmitL(logId, ELogChangeTypeEventAdded, changeIndex);

					// Update the type to indicate that this change action
					// really resulted in an addition
					type = ELogChangeTypeEventAdded;
					}
				}
			else
				{
				changeIndex = iViewContents.Find(logId);

				// If it used to be in the view then it's been removed because it's 
				// not in there anymore
				if	(changeIndex >= KErrNone)
					{
					iViewContents.Remove(changeIndex);
					iChangeManager->ChangeTransactionSubmitL(logId, ELogChangeTypeEventDeleted, changeIndex);

					// Update the type to indicate that this change action
					// really resulted in a deletion
					type = ELogChangeTypeEventDeleted;
					}
				}
			}
			break;
		case ELogChangeTypeEventDeleted:
			{
			changeIndex = iViewContents.Find(logId);

			// If it used to be in the view then tell it about the deletion
			if	(changeIndex >= KErrNone)
				{
				iViewContents.Remove(changeIndex);
				iChangeManager->ChangeTransactionSubmitL(logId, ELogChangeTypeEventDeleted, changeIndex);
				}
			}
			break;
		case ELogChangeTypeLogCleared:
			{
			RebuildViewL();
			iChangeManager->ChangeTransactionSubmitL(logId, ELogChangeTypeLogCleared, 0);
			}
			break;
		default:
			__ASSERT_DEBUG(EFalse, Panic(ELogUnrecognizedChangeType));
			break;
			}
		}

	CleanupStack::PopAndDestroy(&view);

	// Commit the transaction. Will notify client if necessary
	iChangeManager->ChangeTransactionCommitL();
	}

TBool CLogServViewBase::IsAllowed(TEventOp aEventOp, TUint8 aEventTypeIndex)
	{
	TBool result = ETrue;

	const TLogServCacheTypeEntry& entry = iDatabase.DTICacheTypes().FindById(aEventTypeIndex);
	TUid eventTypeUid = entry.iEventType->Uid();
	TInt count = iStandardTypeSecurityCache.Count();
		
	for(TInt i=0;i<count;++i)
		{
		if (eventTypeUid == iStandardTypeSecurityCache[i].eventType)
			{
			result = (aEventOp == EWriteOp) ? iStandardTypeSecurityCache[i].writeAccess : iStandardTypeSecurityCache[i].readAccess;
			break;
			}
		}
	
	return result;
	}

/////////////////////////////////////////////////////////////////////////////////////////
// -----> CLogServViewLockObserver (source)
/////////////////////////////////////////////////////////////////////////////////////////

CLogServViewLockObserver::CLogServViewLockObserver(MLogServBackupInterface& aBackupInterface)
:	iBackupInterface(aBackupInterface), iLockEvents(KLogViewLockStatusEventGranularity)
	{
	}

CLogServViewLockObserver::~CLogServViewLockObserver()
	{
	iBackupInterface.BIObserverRemove(*this);
	iLockEvents.Close();
	}

void CLogServViewLockObserver::ConstructL()
	{
	// Register for backup events
	iBackupInterface.BIObserverAddL(*this, MLogServBackupInterface::EObjectViewLock);
	}

CLogServViewLockObserver* CLogServViewLockObserver::NewL(MLogServBackupInterface& aBackupInterface)
	{
	CLogServViewLockObserver* self = new(ELeave) CLogServViewLockObserver(aBackupInterface);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

void CLogServViewLockObserver::BOHandleEventL(TLogServBackupEvent aEvent)
	{
	// Map event
	TLogViewLockStatus status = ELogViewWindowOpen;
	if	(aEvent == MLogServBackupObserver::EBackupStarting)
		status = ELogViewWindowLocked;

	// Cache or complete immediately
	if	(!HaveLockStatusChangePointer())
		User::LeaveIfError(iLockEvents.Append(status));
	else
		CompleteLockStatusChangeMessage(status);
	}

/**
The client has requested lock status change notifications
*/
void CLogServViewLockObserver::RequestLockStatusChanges(const RMessage2& aMessage)
	{
	if	(!HaveLockStatusChangePointer())
		{
		iLockStatusChangeMessage = aMessage;
		
		// Already have one cached event
		if	(iLockEvents.Count())
			{
			CompleteLockStatusChangeMessage(iLockEvents[0]);
			iLockEvents.Remove(0);
			}
		}
	else
		PanicClient(aMessage, ELogViewLockStatusChangeRequestAlreadyIssued);
	}

/**
The client has cancelled a previous change notification request
*/
void CLogServViewLockObserver::RequestLockStatusChangesCancel()
	{
	if	(HaveLockStatusChangePointer())
		CompleteLockStatusChangeMessage(KErrCancel);
	}

void  CLogServViewLockObserver::CompleteLockStatusChangeMessage(TInt aCompletionCode)
	{
	__ASSERT_ALWAYS(HaveLockStatusChangePointer(), Panic(ELogViewNoLockStatusChangeMessage));
	iLockStatusChangeMessage.Complete(aCompletionCode);
	}

/////////////////////////////////////////////////////////////////////////////////////////
// -----> CLogServViewEvent (source)
/////////////////////////////////////////////////////////////////////////////////////////
CLogServViewEvent::CLogServViewEvent(MLogServDatabaseTransactionInterface& aDatabase, MLogServBackupInterface& aBackupInterface, CLogPackage& aPackage, TLogViewId aViewId, const RMessage2& aMessage)
:	CLogServViewBase(aDatabase, aBackupInterface, aPackage, ELogViewTypeEvent, aViewId, aMessage)
	{
	}

CLogServViewEvent* CLogServViewEvent::NewL(MLogServDatabaseTransactionInterface& aDatabase, MLogServBackupInterface& aBackupInterface, CLogPackage& aPackage, TLogViewId aViewId, const RMessage2& aMessage)
	{
	CLogServViewEvent* self = new(ELeave) CLogServViewEvent(aDatabase, aBackupInterface, aPackage, aViewId, aMessage);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

/**
Setup the view
Fetch the client-side SQL query string which this view uses to obtain a data-set.
*/
TPtrC CLogServViewEvent::GetQueryStringL(const RMessage2& aMessage, TLogFilterConstructionType aFilterType) 
	{
	// Read stuff from the client
	iPackage.ResizeL(aMessage.GetDesLengthL(2));
	aMessage.ReadL(2, iPackage.Ptr());

	// Decode the parameters we've read from the client
	CLogFilterList* filter = new(ELeave)CLogFilterList;
	CleanupStack::PushL(TCleanupItem(DestroyList, filter));
	iPackage.GetLogFilterListL(*filter);
	RLogDynBuf expr;
    TLogFilterExprBuilder exprBuilder(iDatabase);
    exprBuilder.BuildExprLC(expr, *filter, KLogWhere, aFilterType);
    // Generate the query string that will be used
    TheSql.Format(KLogSqlEventViewString, &KLogViewSelectColList, &expr.DesC());
    CleanupStack::PopAndDestroy(2, filter);
	return TheSql;
	}

/////////////////////////////////////////////////////////////////////////////////////////
// -----> CLogServViewRecent (source)
/////////////////////////////////////////////////////////////////////////////////////////
CLogServViewRecent::CLogServViewRecent(MLogServDatabaseTransactionInterface& aDatabase, MLogServBackupInterface& aBackupInterface, CLogPackage& aPackage, TLogViewId aViewId, const RMessage2& aMessage)
:	CLogServViewBase(aDatabase, aBackupInterface, aPackage, ELogViewTypeRecent, aViewId, aMessage)
	{
	}

CLogServViewRecent* CLogServViewRecent::NewL(MLogServDatabaseTransactionInterface& aDatabase, MLogServBackupInterface& aBackupInterface, CLogPackage& aPackage, TLogViewId aViewId, const RMessage2& aMessage)
	{
	CLogServViewRecent* self = new(ELeave) CLogServViewRecent(aDatabase, aBackupInterface, aPackage, aViewId, aMessage);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

/**
Setup the view
Fetch the client-side SQL query string which this view uses to obtain a data-set.
*/
TPtrC CLogServViewRecent::GetQueryStringL(const RMessage2& aMessage, TLogFilterConstructionType aFilterType)
	{
	// Read stuff from the client
	iPackage.ResizeL(aMessage.GetDesLengthL(2));
	aMessage.ReadL(2, iPackage.Ptr());

	// Decode the parameters we've read from the client
	CLogFilterList* filter = new(ELeave)CLogFilterList;
	CleanupStack::PushL(TCleanupItem(DestroyList, filter));
	iPackage.GetLogFilterListL(*filter);
	RLogDynBuf expr;
	TLogFilterExprBuilder exprBuilder(iDatabase);
	exprBuilder.BuildExprLC(expr, *filter, KLogAnd, aFilterType);
    iRecentList = static_cast<TLogRecentList>(aMessage.Int3());
    // Generate the view that will be used
    if (iRecentList == KLogNullRecentList)
        {
        TheSql.Format(KLogSqlAllRecentViewString, &KLogViewSelectColList, &expr.DesC());
        }
    else
        {
        TheSql.Format(KLogSqlRecentViewString, &KLogViewSelectColList, iRecentList, &expr.DesC());
        }
    CleanupStack::PopAndDestroy(2, filter);
	return TheSql;
	}

/**
Remove an entry from the view.
Note the client can call this even if the view isn't valid.
*/
void CLogServViewRecent::RemoveL(const RMessage2& aMessage)
	{
	if(! aMessage.HasCapability(ECapabilityWriteDeviceData))
		User::Leave(KErrPermissionDenied);
		
	const TLogId id = static_cast<TLogId>(aMessage.Int2());
    TBuf<20> num;
    num.AppendNum(id);

	iDatabase.DTIBeginWithRollBackProtectionLC();
	
    TheSql.Copy(KLogSqlRemoveDuplicateEvents);
    TheSql.Append(KIdEqStr);
    TheSql.Append(num);
    TheSql.Append(KLogOr);
    TheSql.Append(KDuplicateEqStr);
    TheSql.Append(num);
    User::LeaveIfError(iDatabase.DTIExecuteSql(TheSql));

	// This is a "hidden" change. It may affect the contents of a view, but the actual event hasn't changed
	iDatabase.DTIChangeInterface().DCISubmitChangedEventContextL(ELogChangeTypeEventChangedHidden, id);
	iDatabase.DTICommitAndCancelRollbackProtectionL();
	}

/**
Clear all duplicate events associated with this recent list.
*/
void CLogServViewRecent::ClearDuplicatesL(const RMessage2& aMessage)
	{
	if(! aMessage.HasCapability(ECapabilityWriteDeviceData))
		User::Leave(KErrPermissionDenied);
	
	if	(iRecentList > 0)
		{
		// Get list of duplicates
		TheSql.Format(KLogSqlSelectAllDuplicatesString, iRecentList);
        RLogDbView view;
        view.PrepareLC(iDatabase.DTIDatabase(), TheSql);
        InitializeColumns2L(view);
		if(view.FirstL())
			{
            iDatabase.DTIBeginWithRollBackProtectionLC();
			// Iterate through the events
			do
				{
				// Get current event id
				view.GetL();
				const TLogId id = view.ColInt32(CLogServViewRecent::iIdColNo);
				// Make the change
				view.UpdateL();
				view.SetColNullL(CLogServViewRecent::iRecentColNo);
				view.SetColNullL(CLogServViewRecent::iDuplicateColNo);
				view.PutL();
				// This is a "hidden" change. It may affect the contents of a view, but the actual event hasn't changed
				iDatabase.DTIChangeInterface().DCISubmitChangedEventContextL(ELogChangeTypeEventChangedHidden, id);
				}
			while(view.NextL());
			iDatabase.DTICommitAndCancelRollbackProtectionL();
			}
		CleanupStack::PopAndDestroy(&view);
		}
	else
		::PanicClientL(aMessage, ELogInvalidRecentView);
	}

void CLogServViewRecent::InitializeColumns2L(RDbRowSet& aRowSet)
    {
    if(CLogServViewRecent::iIdColNo == 0)
        {
        CDbColSet* colset = aRowSet.ColSetL();
        CLogServViewRecent::iIdColNo = colset->ColNo(KLogFieldIdString);
        CLogServViewRecent::iRecentColNo = colset->ColNo(KLogFieldEventRecentString);
        CLogServViewRecent::iDuplicateColNo = colset->ColNo(KLogFieldEventDuplicateString);
        delete colset;
        }
    __ASSERT_DEBUG(CLogServViewRecent::iIdColNo > 0 && 
            CLogServViewRecent::iRecentColNo > 0 && 
            CLogServViewRecent::iDuplicateColNo > 0, User::Invariant());  
        
    }

/////////////////////////////////////////////////////////////////////////////////////////
// -----> CLogServViewDuplicate (source)
/////////////////////////////////////////////////////////////////////////////////////////
CLogServViewDuplicate::CLogServViewDuplicate(MLogServDatabaseTransactionInterface& aDatabase, MLogServBackupInterface& aBackupInterface, CLogPackage& aPackage, TLogViewId aViewId, const RMessage2& aMessage)
:	CLogServViewBase(aDatabase, aBackupInterface, aPackage, ELogViewTypeDuplicate, aViewId, aMessage)
//
//	Duplicate view
//
	{
	}

CLogServViewDuplicate* CLogServViewDuplicate::NewL(MLogServDatabaseTransactionInterface& aDatabase, MLogServBackupInterface& aBackupInterface, CLogPackage& aPackage, TLogViewId aViewId, const RMessage2& aMessage)
	{
	CLogServViewDuplicate* self = new(ELeave) CLogServViewDuplicate(aDatabase, aBackupInterface, aPackage, aViewId, aMessage);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

/**
Setup the view
Fetch the client-side SQL query string which this view uses to obtain a data-set.
*/
TPtrC CLogServViewDuplicate::GetQueryStringL(const RMessage2& aMessage, TLogFilterConstructionType aFilterType)
	{
	// Read stuff from the client
	iPackage.ResizeL(aMessage.GetDesLengthL(2));
	aMessage.ReadL(2, iPackage.Ptr());

	// Decode the parameters we've read from the client
	CLogFilterList* filter = new(ELeave)CLogFilterList;
	CleanupStack::PushL(TCleanupItem(DestroyList, filter));
	iPackage.GetLogFilterListL(*filter);
	RLogDynBuf expr;
	TLogFilterExprBuilder exprBuilder(iDatabase);
	exprBuilder.BuildExprLC(expr, *filter, KLogAnd, aFilterType);
	iSourceId = (TInt)aMessage.Ptr3();
    // Generate the view that will be used
    TheSql.Format(KLogSqlDuplicateViewString2, &KLogViewSelectColList, iSourceId, &expr.DesC());
    CleanupStack::PopAndDestroy(2, filter);
	return TheSql;
	}

/**
Remove an entry from the view.
*/
void CLogServViewDuplicate::RemoveL(const RMessage2& aMessage)
	{
	if(! aMessage.HasCapability(ECapabilityWriteDeviceData))
		User::Leave(KErrPermissionDenied);

	const TLogId id = static_cast<TLogId>(aMessage.Int2());

	iDatabase.DTIBeginWithRollBackProtectionLC();

	// Do the actual work
	TheSql.Format(KLogSqlRemoveDuplicateString, id, iSourceId);
	const TInt error = iDatabase.DTIExecuteSql(TheSql);
	User::LeaveIfError(error);
	//
	// This is a "hidden" change. It may affect the contents of a view, but the actual event hasn't changed
	iDatabase.DTIChangeInterface().DCISubmitChangedEventContextL(ELogChangeTypeEventChangedHidden, id);
	iDatabase.DTICommitAndCancelRollbackProtectionL();
	}