sl@0: // Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0: // All rights reserved.
sl@0: // This component and the accompanying materials are made available
sl@0: // under the terms of "Eclipse Public License v1.0"
sl@0: // which accompanies this distribution, and is available
sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0: //
sl@0: // Initial Contributors:
sl@0: // Nokia Corporation - initial contribution.
sl@0: //
sl@0: // Contributors:
sl@0: //
sl@0: // Description:
sl@0: //
sl@0: 
sl@0: #include "LogServCacheStrings.h"
sl@0: #include "LogServDatabaseTransactionInterface.h"
sl@0: #include "logservpanic.h"
sl@0: #include "LogServSqlStrings.h"
sl@0: 
sl@0: /**
sl@0: Strings array granularity.
sl@0: @internalComponent
sl@0: */
sl@0: const TInt KLogNumberOfInitialStrings = 16;
sl@0: 
sl@0: /////////////////////////////////////////////////////////////////////////////////////////
sl@0: // -----> CLogServCacheStrings (source)
sl@0: /////////////////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: CLogServCacheStrings::CLogServCacheStrings(MLogServDatabaseTransactionInterface& aDatabase) :
sl@0: 	iDatabase(aDatabase), 
sl@0: 	iStrings(KLogNumberOfInitialStrings)
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: CLogServCacheStrings::~CLogServCacheStrings()
sl@0: 	{
sl@0: 	DestroyCache();
sl@0: 	}
sl@0: 
sl@0: void CLogServCacheStrings::DestroyCache()
sl@0: 	{
sl@0: 	for(TInt i=iStrings.Count()-1;i>=0;--i)
sl@0: 		{
sl@0: 		TLogServCacheStringEntry::DeleteEntry(iStrings[i]);
sl@0: 		}
sl@0: 	iStrings.Close();
sl@0: 	}
sl@0: 
sl@0: void CLogServCacheStrings::ConstructL()
sl@0: 	{
sl@0: 	ReadStringsFromDatabaseL();
sl@0: 	}
sl@0: 
sl@0: CLogServCacheStrings* CLogServCacheStrings::NewL(MLogServDatabaseTransactionInterface& aDatabase)
sl@0: 	{
sl@0: 	CLogServCacheStrings* self = new(ELeave) CLogServCacheStrings(aDatabase);
sl@0: 	CleanupStack::PushL(self);
sl@0: 	self->ConstructL();
sl@0: 	CleanupStack::Pop(self);
sl@0: 	return self;
sl@0: 	}
sl@0: 
sl@0: /////////////////////////////////////////////////////////////////////////////////////////
sl@0: /////////////////////////////////////////////////////////////////////////////////////////
sl@0: /////////////////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: /**
sl@0: Find the descriptor corresponding to a given string id. KNullDesC if the id is not found.
sl@0: */
sl@0: const TPtrC CLogServCacheStrings::FindString(TLogStringId aId) const
sl@0: 	{
sl@0: 	if(aId != KLogNullStringId)
sl@0: 		{
sl@0: 		for(TInt i=iStrings.Count()-1;i>=0;--i)
sl@0: 			{
sl@0: 			if(iStrings[i]->iId == aId)
sl@0: 				{
sl@0: 				return iStrings[i]->String();	
sl@0: 				}
sl@0: 			}
sl@0: 		}
sl@0: 	return KNullDesC();
sl@0: 	}
sl@0: 
sl@0: 
sl@0: /**
sl@0: Find the id of a given string. KLogNullStringId if no string is found.
sl@0: */
sl@0: TLogStringId CLogServCacheStrings::FindId(const TDesC& aString)
sl@0: 	{
sl@0: 	if(aString.Length() == 0)
sl@0: 		{
sl@0: 		return KLogNullStringId;	
sl@0: 		}
sl@0: 	TInt position = iStrings.FindInOrder(aString, &CLogServCacheStrings::Compare1);
sl@0: 	return position >= 0 ? iStrings[position]->iId : KLogNullStringId;
sl@0: 	}
sl@0: 
sl@0: /**
sl@0: Find the id of a given string. Add the string to the cache if the string is not there.
sl@0: If the aString length is 0, then do not search the cache, do not add the string, just return KLogNullStringId.
sl@0: If the string has to be added - the string will be added to the cache and
sl@0: a new record will be inserted into the database.
sl@0: If the database is in transaction, the string in the cache will have its "dirty" flag set. During the rollback
sl@0: (if a rollback occurs) all strings with "dirty" flag set will be removed from the cache.
sl@0: The idea is to keep the cache content consistent with the database content.
sl@0: */
sl@0: TLogStringId CLogServCacheStrings::GetIdL(const TDesC& aString)
sl@0: 	{
sl@0: 	if(aString.Length() == 0)
sl@0: 		{
sl@0: 		return KLogNullStringId;	
sl@0: 		}
sl@0: 	TLogStringId id = FindId(aString);
sl@0: 	if(id == KLogNullStringId)
sl@0: 		{
sl@0: 		id = DoAddStringL(aString);	
sl@0: 		}
sl@0: 	return id;
sl@0: 	}
sl@0: 
sl@0: /**
sl@0: Clears the dirty flag of the cache entries that have been added during the last transaction.
sl@0: */
sl@0: void CLogServCacheStrings::Commit()
sl@0: 	{
sl@0: 	if(iDirty)
sl@0: 		{
sl@0: 		for(TInt i=iStrings.Count()-1;i>=0;--i)
sl@0: 			{
sl@0: 			iStrings[i]->iDirty = EFalse;
sl@0: 			}
sl@0: 		iDirty = EFalse;	
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: 
sl@0: /**
sl@0: Removes any strings added to the cache of strings since the beginning of last transaction (with iDirty flag set).
sl@0: */
sl@0: void CLogServCacheStrings::Rollback()
sl@0: 	{
sl@0: 	if(iDirty)
sl@0: 		{
sl@0: 		for(TInt i=iStrings.Count()-1;i>=0;--i)
sl@0: 			{
sl@0: 			if(iStrings[i]->iDirty)
sl@0: 				{
sl@0: 				TLogServCacheStringEntry::DeleteEntry(iStrings[i]);
sl@0: 				iStrings.Remove(i);
sl@0: 				}
sl@0: 			}
sl@0: 		iDirty = EFalse;	
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: /////////////////////////////////////////////////////////////////////////////////////////
sl@0: /////////////////////////////////////////////////////////////////////////////////////////
sl@0: /////////////////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: void CLogServCacheStrings::ReadStringsFromDatabaseL()
sl@0: 	{
sl@0: 	DestroyCache();
sl@0: 	RDbTable tbl;
sl@0: 	CleanupClosePushL(tbl);
sl@0: 	User::LeaveIfError(tbl.Open(iDatabase.DTIDatabase(), KLogNameStringString, RDbRowSet::EReadOnly));
sl@0: 	if(tbl.FirstL())
sl@0: 		{
sl@0: 		InitializeColNumsL(tbl);
sl@0: 		iStrings.ReserveL(tbl.CountL());
sl@0: 		do
sl@0: 			{
sl@0: 			tbl.GetL();			
sl@0: 			const TLogStringId id = tbl.ColInt16(iIdColNo);
sl@0: 			const TPtrC pString(tbl.ColDes(iStringColNo));
sl@0: 			TLinearOrder<TLogServCacheStringEntry*> orderer(&CLogServCacheStrings::Compare2);
sl@0: 			TLogServCacheStringEntry* entry = TLogServCacheStringEntry::NewEntryL(id, pString);
sl@0: 			TInt err = iStrings.InsertInOrder(entry, orderer);
sl@0: 			__ASSERT_ALWAYS(err == KErrNone, Panic(ELogStringsCacheReserved));
sl@0: 			}
sl@0: 		while(tbl.NextL());		
sl@0: 		}
sl@0: 	CleanupStack::PopAndDestroy(&tbl);
sl@0: 	}
sl@0: 
sl@0: //Atomic "add string to cache" operation.
sl@0: TLogStringId CLogServCacheStrings::DoAddStringL(const TDesC& aString)
sl@0: 	{
sl@0: 	//Reserve space for the new string in the cache
sl@0: 	iStrings.ReserveL(iStrings.Count() + 1);
sl@0: 	//Open the database table and push it on the cleanup stack.
sl@0: 	//If the InitializeColNumsL() operation leaves then the table will be closed automatically.
sl@0: 	RDbTable tbl;
sl@0: 	CleanupClosePushL(tbl);
sl@0: 	User::LeaveIfError(tbl.Open(iDatabase.DTIDatabase(), KLogNameStringString));
sl@0: 	InitializeColNumsL(tbl);
sl@0: 	//Allocate space for the new record in the table. Push the Cancel() function on the cleanup stack.
sl@0: 	//If some of the next calls leaves, the insert operation will be automatically cancelled.
sl@0: 	tbl.InsertL();
sl@0: 	const TLogStringId id = tbl.ColInt16(iIdColNo);
sl@0: 	tbl.SetColL(iStringColNo, aString);
sl@0: 	//Create new cache entry and push it on the cleanup stack.
sl@0: 	TLogServCacheStringEntry* entry = TLogServCacheStringEntry::NewEntryLC(id, aString, iDatabase.DTIInTransaction());
sl@0: 	//Finish the "insert record" operation. If PutL() leaves, then:
sl@0: 	//  - the new cache entry is deleted
sl@0: 	//  - the insert operation is cancelled
sl@0: 	//  - the table is closed
sl@0: 	tbl.PutL();
sl@0: 	//Pop the entry and the Cancel() from the cleanup stack. Close the table.
sl@0: 	CleanupStack::Pop();//TLogServCacheStringEntry
sl@0: 	CleanupStack::PopAndDestroy(&tbl);
sl@0: 	//The next operation is guaranteed to be a non-failing "add entry" operation.
sl@0: 	TLinearOrder<TLogServCacheStringEntry*> orderer(&CLogServCacheStrings::Compare2);
sl@0: 	TInt err = iStrings.InsertInOrder(entry, orderer);
sl@0: 	__ASSERT_ALWAYS(err == KErrNone, Panic(ELogStringsCacheReserved));
sl@0: 	//Mark the cache as dirty. Later if there was an outstanding transaction and that transaction failed, 
sl@0: 	//The database rollback operation will restore the original state of the table. 
sl@0: 	//The CLogServCacheStrings::RollbackAddStringsL() will remove from the cache all "dirty" strings.
sl@0: 	iDirty = ETrue;
sl@0: 	return id;
sl@0: 	}
sl@0: 
sl@0: TInt CLogServCacheStrings::Compare1(const TDesC* aString, TLogServCacheStringEntry* const& aRight)
sl@0: 	{
sl@0: 	__ASSERT_DEBUG(aString != NULL, Panic(ELogStringsCacheNullArg1));
sl@0: 	__ASSERT_DEBUG(aRight != NULL, Panic(ELogStringsCacheNullArg1));
sl@0: 	return aString->Compare(aRight->String());
sl@0: 	}
sl@0: 
sl@0: TInt CLogServCacheStrings::Compare2(TLogServCacheStringEntry* const& aLeft, TLogServCacheStringEntry* const& aRight)
sl@0: 	{
sl@0: 	__ASSERT_DEBUG(aLeft != NULL, Panic(ELogStringsCacheNullArg2));
sl@0: 	__ASSERT_DEBUG(aRight != NULL, Panic(ELogStringsCacheNullArg2));
sl@0: 	return aLeft->String().Compare(aRight->String());
sl@0: 	}
sl@0: 
sl@0: void CLogServCacheStrings::InitializeColNumsL(RDbRowSet& aRowSet)
sl@0: 	{
sl@0: 	if(iIdColNo == 0)
sl@0: 		{
sl@0: 		CDbColSet* colset = aRowSet.ColSetL();
sl@0: 		iIdColNo = colset->ColNo(KLogFieldStringIdString);
sl@0: 		iStringColNo = colset->ColNo(KLogFieldStringTextString);
sl@0: 		delete colset;
sl@0: 		}
sl@0: 	__ASSERT_DEBUG(iIdColNo > 0, Panic(ELogInvalidStringColNo));
sl@0: 	__ASSERT_DEBUG(iStringColNo > 0, Panic(ELogInvalidStringColNo));
sl@0: 	}
sl@0: 
sl@0: void CLogServCacheStrings::TLogServCacheStringEntry::CleanupEntry(TAny* aEntry)
sl@0: 	{
sl@0: 	TLogServCacheStringEntry* entry = reinterpret_cast <TLogServCacheStringEntry*> (aEntry);
sl@0: 	CLogServCacheStrings::TLogServCacheStringEntry::DeleteEntry(entry);
sl@0: 	}