localisation/apparchitecture/apserv/APSRECCACHE.cpp
branchSymbian2
changeset 1 8758140453c0
child 6 c108117318cb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/localisation/apparchitecture/apserv/APSRECCACHE.cpp	Thu Jan 21 12:53:44 2010 +0000
@@ -0,0 +1,391 @@
+// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Symbian Foundation License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.symbianfoundation.org/legal/sfl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include "APSRECCACHE.h"
+#include "APSSES.H"
+
+//////////////////////////////////////////////////////////////////////////////
+// Constants
+
+const TUint KMaxNumberOfEntries = 200;
+//////////////////////////////////////////////////////////////////////////////
+
+CRecognitionResultHashMapEntry* CRecognitionResultHashMapEntry::NewL(const TDesC& aFileName, TTime aLastModified, const TDataRecognitionResult& aResult, CRecognitionResultHashMapEntry* aNext)
+	{
+	CRecognitionResult* result = CRecognitionResult::NewL(aFileName, aResult);
+	CleanupClosePushL(*result);
+	CRecognitionResultHashMapEntry* self = new(ELeave) CRecognitionResultHashMapEntry(aLastModified, result, aNext);
+	CleanupStack::Pop(result);	
+	return self;
+	}
+
+CRecognitionResultHashMapEntry::CRecognitionResultHashMapEntry(TTime aLastModified, CRecognitionResult* aResult, CRecognitionResultHashMapEntry* aNext)
+	: iLastModified(aLastModified), iResult(aResult), iNext(aNext)
+	{
+	}
+
+CRecognitionResultHashMapEntry::~CRecognitionResultHashMapEntry()
+	{
+	iResult->Close();
+	iResult = NULL;
+	delete iNext;
+	}
+
+void CRecognitionResultHashMapEntry::UpdateL(TTime aLastModified, const TDataRecognitionResult& aResult)
+	{
+	// since other objects  might have a pointer to the CRecognitionResult object
+	// and rely on the value not changing we need to create a new object to take 
+	// its place.
+	
+	CRecognitionResult* result = CRecognitionResult::NewL(FileName(), aResult);
+	iResult->Close();
+	iResult = result;
+	
+	iLastModified = aLastModified;
+	}
+
+///////////////////////////////////////////////////////////////////////
+// CRecognitionResultHashMap
+///////////////////////////////////////////////////////////////////////
+
+CRecognitionResultHashMap::CRecognitionResultHashMap()
+	{
+	}
+
+CRecognitionResultHashMap::~CRecognitionResultHashMap()
+	{
+	for(TUint i = 0; i < KFileHashMapEntries; i++)
+		{
+		delete Entry(i);
+		}
+	}
+
+CRecognitionResult* CRecognitionResultHashMap::Get(const TDesC& aKey, TTime& aLastModified) const
+	{
+	const TUint index = GetIndex(aKey);
+
+	for(const CRecognitionResultHashMapEntry* entry = Entry(index); entry; entry = entry->Next())
+		{
+		if(entry->FileName().Compare(aKey) == 0)
+			{
+			aLastModified = entry->LastModified();
+			return entry->Result();
+			}
+		}
+	return NULL;
+	}
+
+/**
+Adds a recognition result to the hash map of the particular directory.
+This is only done if it isn't already existing.
+The return value specifies if the number of entries was increased by one (ETrue) or not.
+@internalComponent
+*/
+TBool CRecognitionResultHashMap::AddL(const TDesC& aKey, TTime aLastModified, const TDataRecognitionResult& aResult)
+	{
+	const TUint index = GetIndex(aKey);
+
+	//check if there already is an entry for the file
+	for(CRecognitionResultHashMapEntry* entry = Entry(index); entry; entry = entry->Next())
+		{
+		if(entry->FileName().Compare(aKey) == 0)
+			{
+			// already there -> check modification date
+			if( entry->LastModified() != aLastModified )
+				{
+				entry->UpdateL(aLastModified, aResult);
+				}
+
+			return EFalse;
+			}
+		}
+
+	//create new entry
+	SetEntry(index, CRecognitionResultHashMapEntry::NewL(aKey, aLastModified, aResult, Entry(index)));
+	iNumberOfEntries++;
+	return ETrue;
+	}
+	
+TUint CRecognitionResultHashMap::GetIndex(const TDesC& aKey) const
+	{
+	// That's the hash algorithm
+	TUint hash = aKey.Length();
+	for(TUint i=0;i< aKey.Length();i++)
+		{
+		hash += aKey[i];
+		}
+	return hash % KFileHashMapEntries;
+	}
+	
+
+///////////////////////////////////////////////////////////////////////
+// CCacheDirectoryEntry
+///////////////////////////////////////////////////////////////////////
+
+CCacheDirectoryEntry* CCacheDirectoryEntry::NewL(const TDesC& aDirectory)
+	{
+	CCacheDirectoryEntry* self = new(ELeave) CCacheDirectoryEntry;
+	CleanupStack::PushL(self);
+	self->iDirectory = aDirectory.AllocL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CCacheDirectoryEntry::CCacheDirectoryEntry()
+	{
+	}
+
+CCacheDirectoryEntry::~CCacheDirectoryEntry()
+	{
+	delete iDirectory;
+	}
+
+const TInt CCacheDirectoryEntry::iOffset = _FOFF(CCacheDirectoryEntry,iDlink);
+
+///////////////////////////////////////////////////////////////////////
+// CApsRecognitionCache
+///////////////////////////////////////////////////////////////////////
+
+CApsRecognitionCache::CApsRecognitionCache(RFs& aFs)
+	: iFs(aFs),
+	  iDirectoryHeader(CCacheDirectoryEntry::iOffset),
+	  iIter(iDirectoryHeader)
+	{
+	}
+
+CApsRecognitionCache::~CApsRecognitionCache()
+	{
+    CCacheDirectoryEntry* anyitem;
+    
+    iIter.SetToFirst();
+    while ((anyitem = iIter++) != NULL)
+        {
+        anyitem->iDlink.Deque();
+        delete anyitem;
+        };
+	}
+
+/**
+Adds a recognition result to the cache. An object is not added, when an up-to-date
+version is already in the cache or if it's not possible to check the file's LastModified().
+@internalComponent
+*/
+void CApsRecognitionCache::AddL(const TDesC& aDirectory, const TDesC& aFileName, const TDataRecognitionResult& aRecognitionResult)
+	{
+	TTime lastModified;
+	TFileName fullFileName(aDirectory);
+	fullFileName.Append(aFileName);
+	if(iFs.Modified(fullFileName, lastModified) != KErrNone)
+		{
+	#if defined(_DEBUG)
+		RDebug::Print(_L("CApsRecognitionCache::AddL(): File '%S' was not added to the cache, cannot get the modified attribute (full filename is required for the cache to work)."), &fullFileName);
+	#endif
+		return;
+		}
+	DoAddL(aDirectory, aFileName, lastModified, aRecognitionResult);
+	}
+ 
+/**
+Adds a recognition result to the cache. An object is not added, when an up-to-date
+version is already in the cache or if it's not possible to check the file's LastModified().
+@internalComponent
+*/
+void CApsRecognitionCache::AddL(const RFile& aFile, const TDesC& aDirectory, const TDesC& aFileName, const TDataRecognitionResult& aRecognitionResult)
+	{
+	TTime lastModified;
+	if(aFile.Modified(lastModified) != KErrNone)
+		{
+	#if defined(_DEBUG)
+		TFileName fullFileName(aDirectory);
+		fullFileName.Append(aFileName);
+		RDebug::Print(_L("CApsRecognitionCache::AddL(): File '%S' was not added to the cache, cannot get the modified attribute."), &fullFileName);
+	#endif
+		return;
+		}
+	DoAddL(aDirectory, aFileName, lastModified, aRecognitionResult);
+	}
+
+/**
+Adds a recognition result to the cache. An object is not added, when an up-to-date
+version is already in the cache or if it's not possible to check the file's LastModified().
+@internalComponent
+*/
+void CApsRecognitionCache::DoAddL(const TDesC& aDirectory, const TDesC& aFileName, const TTime& aLastModified, const TDataRecognitionResult& aRecognitionResult)
+	{
+	if(iNumberOfEntries > KMaxNumberOfEntries)
+		{
+		Cleanup();
+		}
+		
+	CCacheDirectoryEntry* entry = NULL;
+    iIter.SetToFirst();
+    while ((entry = iIter++) != NULL)
+    	{
+		if(CompareDirectories(entry->Directory(),aDirectory))
+			{
+			// move directory to top (it is likely to be used again soon)
+			entry->iDlink.Deque();
+			iDirectoryHeader.AddFirst(*entry);
+			break;
+			}
+    	}
+
+	if(!entry)
+		{
+		// create directory and append it
+		entry = CCacheDirectoryEntry::NewL(aDirectory);
+		iDirectoryHeader.AddFirst(*entry);
+		}
+
+	// insert to correct directory
+	if(entry->Files().AddL(aFileName, aLastModified, aRecognitionResult))
+		{
+		iNumberOfEntries++;
+		}
+	}
+
+/**
+Searches the cache for the particular recognition result. If the file was not modified since the
+file was recognized, the stored recognition result is returned.
+@internalComponent
+*/
+TBool CApsRecognitionCache::Get(const TDesC& aDirectory, const TDesC& aFileName, TDataRecognitionResult& aRecognitionResult)
+	{
+	TTime lastModified;
+	TFileName fileName(aDirectory);
+	fileName.Append(aFileName);
+	const TInt error = iFs.Modified(fileName, lastModified);
+	if(error != KErrNone) 
+		{
+		return EFalse;
+		}
+
+	CRecognitionResult* result = DoGet(aDirectory, aFileName, lastModified);
+	if(result)
+		{
+		result->Get(aRecognitionResult);
+		result->Close(); // decrease reference count since we're not exposing the object
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+/**
+Searches the cache for the particular recognition result. If the file was not modified since the
+file was recognized, the stored recognition result is returned.
+
+N.B. The returned value is reference counted!
+@internalComponent
+*/
+CRecognitionResult* CApsRecognitionCache::Get(const RFile& aFile, const TDesC& aDirectory, const TDesC& aFileName)
+	{	
+	TTime lastModified;
+	const TInt error = aFile.Modified(lastModified);
+	if(error != KErrNone)
+		{
+		return NULL;
+		}
+
+	return DoGet(aDirectory, aFileName, lastModified);
+	}
+
+/**
+Searches the cache for the particular recognition result. If the file was not modified since the
+file was recognized, the stored recognition result is returned.
+
+N.B. The returned value is reference counted!
+@internalComponent
+*/
+CRecognitionResult* CApsRecognitionCache::DoGet(const TDesC& aDirectory, const TDesC& aFileName, const TTime& aLastModified)
+	{	
+	CRecognitionResult* result = NULL;
+	CCacheDirectoryEntry* entry;
+
+    iIter.SetToFirst();
+    while ((entry = iIter++) != NULL)
+    	{
+    	if(CompareDirectories(entry->Directory(),aDirectory))
+    		{
+    		TTime cachedLastModified;
+			result = entry->Files().Get(aFileName, cachedLastModified);
+			if(result)
+				{
+				if(cachedLastModified != aLastModified)
+					{
+					result->Close();
+					result = NULL;
+					}
+				}
+
+			// move directory to top (it is likely to be used again soon)
+			entry->iDlink.Deque();
+			iDirectoryHeader.AddFirst(*entry);
+			break;
+    		}
+    	}
+	return result;
+	}
+
+/**
+A fast comparison of two directory names.
+Should be faster than the "normal" TDesC16::Compare function, because
+there is no need to check which string is "greater".
+@internalComponent
+*/
+TBool CApsRecognitionCache::CompareDirectories(const TDesC& aDir1, const TDesC& aDir2) const
+	{
+	TBool equal = ETrue;
+	if(aDir1.Length() == aDir2.Length())
+		{
+		// start with the last character, as directories tend to be the same at the beginning
+		for(TInt i=aDir1.Length()-1; i>=0; i--)
+			{
+			if(aDir1[i] != aDir2[i])
+				{
+				// return as soon one character is "wrong"
+				equal = EFalse;
+				break;
+				}
+			}
+		}
+	else
+		{
+		equal = EFalse;
+		}
+	return equal;
+	}
+/**
+Removes the least-often used directory in the cache.
+@internalComponent
+*/
+void CApsRecognitionCache::Cleanup()
+	{
+    CCacheDirectoryEntry* item = iDirectoryHeader.Last();
+    iNumberOfEntries -= item->NumberOfEntries();
+    item->iDlink.Deque();
+    delete item;
+	}
+
+/**
+Empties the cache. Useful mainly for testing.
+@internalComponent
+*/
+void CApsRecognitionCache::Flush()
+	{
+	while(!iDirectoryHeader.IsEmpty())
+		{
+		Cleanup();
+		}
+	}