loggingservices/eventlogger/LogServ/src/LogServCacheStrings.cpp
changeset 0 08ec8eefde2f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loggingservices/eventlogger/LogServ/src/LogServCacheStrings.cpp	Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,256 @@
+// 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 "LogServCacheStrings.h"
+#include "LogServDatabaseTransactionInterface.h"
+#include "logservpanic.h"
+#include "LogServSqlStrings.h"
+
+/**
+Strings array granularity.
+@internalComponent
+*/
+const TInt KLogNumberOfInitialStrings = 16;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// -----> CLogServCacheStrings (source)
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CLogServCacheStrings::CLogServCacheStrings(MLogServDatabaseTransactionInterface& aDatabase) :
+	iDatabase(aDatabase), 
+	iStrings(KLogNumberOfInitialStrings)
+	{
+	}
+
+CLogServCacheStrings::~CLogServCacheStrings()
+	{
+	DestroyCache();
+	}
+
+void CLogServCacheStrings::DestroyCache()
+	{
+	for(TInt i=iStrings.Count()-1;i>=0;--i)
+		{
+		TLogServCacheStringEntry::DeleteEntry(iStrings[i]);
+		}
+	iStrings.Close();
+	}
+
+void CLogServCacheStrings::ConstructL()
+	{
+	ReadStringsFromDatabaseL();
+	}
+
+CLogServCacheStrings* CLogServCacheStrings::NewL(MLogServDatabaseTransactionInterface& aDatabase)
+	{
+	CLogServCacheStrings* self = new(ELeave) CLogServCacheStrings(aDatabase);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+Find the descriptor corresponding to a given string id. KNullDesC if the id is not found.
+*/
+const TPtrC CLogServCacheStrings::FindString(TLogStringId aId) const
+	{
+	if(aId != KLogNullStringId)
+		{
+		for(TInt i=iStrings.Count()-1;i>=0;--i)
+			{
+			if(iStrings[i]->iId == aId)
+				{
+				return iStrings[i]->String();	
+				}
+			}
+		}
+	return KNullDesC();
+	}
+
+
+/**
+Find the id of a given string. KLogNullStringId if no string is found.
+*/
+TLogStringId CLogServCacheStrings::FindId(const TDesC& aString)
+	{
+	if(aString.Length() == 0)
+		{
+		return KLogNullStringId;	
+		}
+	TInt position = iStrings.FindInOrder(aString, &CLogServCacheStrings::Compare1);
+	return position >= 0 ? iStrings[position]->iId : KLogNullStringId;
+	}
+
+/**
+Find the id of a given string. Add the string to the cache if the string is not there.
+If the aString length is 0, then do not search the cache, do not add the string, just return KLogNullStringId.
+If the string has to be added - the string will be added to the cache and
+a new record will be inserted into the database.
+If the database is in transaction, the string in the cache will have its "dirty" flag set. During the rollback
+(if a rollback occurs) all strings with "dirty" flag set will be removed from the cache.
+The idea is to keep the cache content consistent with the database content.
+*/
+TLogStringId CLogServCacheStrings::GetIdL(const TDesC& aString)
+	{
+	if(aString.Length() == 0)
+		{
+		return KLogNullStringId;	
+		}
+	TLogStringId id = FindId(aString);
+	if(id == KLogNullStringId)
+		{
+		id = DoAddStringL(aString);	
+		}
+	return id;
+	}
+
+/**
+Clears the dirty flag of the cache entries that have been added during the last transaction.
+*/
+void CLogServCacheStrings::Commit()
+	{
+	if(iDirty)
+		{
+		for(TInt i=iStrings.Count()-1;i>=0;--i)
+			{
+			iStrings[i]->iDirty = EFalse;
+			}
+		iDirty = EFalse;	
+		}
+	}
+
+
+/**
+Removes any strings added to the cache of strings since the beginning of last transaction (with iDirty flag set).
+*/
+void CLogServCacheStrings::Rollback()
+	{
+	if(iDirty)
+		{
+		for(TInt i=iStrings.Count()-1;i>=0;--i)
+			{
+			if(iStrings[i]->iDirty)
+				{
+				TLogServCacheStringEntry::DeleteEntry(iStrings[i]);
+				iStrings.Remove(i);
+				}
+			}
+		iDirty = EFalse;	
+		}
+	}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CLogServCacheStrings::ReadStringsFromDatabaseL()
+	{
+	DestroyCache();
+	RDbTable tbl;
+	CleanupClosePushL(tbl);
+	User::LeaveIfError(tbl.Open(iDatabase.DTIDatabase(), KLogNameStringString, RDbRowSet::EReadOnly));
+	if(tbl.FirstL())
+		{
+		InitializeColNumsL(tbl);
+		iStrings.ReserveL(tbl.CountL());
+		do
+			{
+			tbl.GetL();			
+			const TLogStringId id = tbl.ColInt16(iIdColNo);
+			const TPtrC pString(tbl.ColDes(iStringColNo));
+			TLinearOrder<TLogServCacheStringEntry*> orderer(&CLogServCacheStrings::Compare2);
+			TLogServCacheStringEntry* entry = TLogServCacheStringEntry::NewEntryL(id, pString);
+			TInt err = iStrings.InsertInOrder(entry, orderer);
+			__ASSERT_ALWAYS(err == KErrNone, Panic(ELogStringsCacheReserved));
+			}
+		while(tbl.NextL());		
+		}
+	CleanupStack::PopAndDestroy(&tbl);
+	}
+
+//Atomic "add string to cache" operation.
+TLogStringId CLogServCacheStrings::DoAddStringL(const TDesC& aString)
+	{
+	//Reserve space for the new string in the cache
+	iStrings.ReserveL(iStrings.Count() + 1);
+	//Open the database table and push it on the cleanup stack.
+	//If the InitializeColNumsL() operation leaves then the table will be closed automatically.
+	RDbTable tbl;
+	CleanupClosePushL(tbl);
+	User::LeaveIfError(tbl.Open(iDatabase.DTIDatabase(), KLogNameStringString));
+	InitializeColNumsL(tbl);
+	//Allocate space for the new record in the table. Push the Cancel() function on the cleanup stack.
+	//If some of the next calls leaves, the insert operation will be automatically cancelled.
+	tbl.InsertL();
+	const TLogStringId id = tbl.ColInt16(iIdColNo);
+	tbl.SetColL(iStringColNo, aString);
+	//Create new cache entry and push it on the cleanup stack.
+	TLogServCacheStringEntry* entry = TLogServCacheStringEntry::NewEntryLC(id, aString, iDatabase.DTIInTransaction());
+	//Finish the "insert record" operation. If PutL() leaves, then:
+	//  - the new cache entry is deleted
+	//  - the insert operation is cancelled
+	//  - the table is closed
+	tbl.PutL();
+	//Pop the entry and the Cancel() from the cleanup stack. Close the table.
+	CleanupStack::Pop();//TLogServCacheStringEntry
+	CleanupStack::PopAndDestroy(&tbl);
+	//The next operation is guaranteed to be a non-failing "add entry" operation.
+	TLinearOrder<TLogServCacheStringEntry*> orderer(&CLogServCacheStrings::Compare2);
+	TInt err = iStrings.InsertInOrder(entry, orderer);
+	__ASSERT_ALWAYS(err == KErrNone, Panic(ELogStringsCacheReserved));
+	//Mark the cache as dirty. Later if there was an outstanding transaction and that transaction failed, 
+	//The database rollback operation will restore the original state of the table. 
+	//The CLogServCacheStrings::RollbackAddStringsL() will remove from the cache all "dirty" strings.
+	iDirty = ETrue;
+	return id;
+	}
+
+TInt CLogServCacheStrings::Compare1(const TDesC* aString, TLogServCacheStringEntry* const& aRight)
+	{
+	__ASSERT_DEBUG(aString != NULL, Panic(ELogStringsCacheNullArg1));
+	__ASSERT_DEBUG(aRight != NULL, Panic(ELogStringsCacheNullArg1));
+	return aString->Compare(aRight->String());
+	}
+
+TInt CLogServCacheStrings::Compare2(TLogServCacheStringEntry* const& aLeft, TLogServCacheStringEntry* const& aRight)
+	{
+	__ASSERT_DEBUG(aLeft != NULL, Panic(ELogStringsCacheNullArg2));
+	__ASSERT_DEBUG(aRight != NULL, Panic(ELogStringsCacheNullArg2));
+	return aLeft->String().Compare(aRight->String());
+	}
+
+void CLogServCacheStrings::InitializeColNumsL(RDbRowSet& aRowSet)
+	{
+	if(iIdColNo == 0)
+		{
+		CDbColSet* colset = aRowSet.ColSetL();
+		iIdColNo = colset->ColNo(KLogFieldStringIdString);
+		iStringColNo = colset->ColNo(KLogFieldStringTextString);
+		delete colset;
+		}
+	__ASSERT_DEBUG(iIdColNo > 0, Panic(ELogInvalidStringColNo));
+	__ASSERT_DEBUG(iStringColNo > 0, Panic(ELogInvalidStringColNo));
+	}
+
+void CLogServCacheStrings::TLogServCacheStringEntry::CleanupEntry(TAny* aEntry)
+	{
+	TLogServCacheStringEntry* entry = reinterpret_cast <TLogServCacheStringEntry*> (aEntry);
+	CLogServCacheStrings::TLogServCacheStringEntry::DeleteEntry(entry);
+	}