symhelp/helpmodel/tsrc/TLoader.cpp
changeset 0 1f04cf54edd8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/symhelp/helpmodel/tsrc/TLoader.cpp	Tue Jan 26 15:15:23 2010 +0200
@@ -0,0 +1,546 @@
+// Copyright (c) 1999-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:
+//
+
+// System includes
+#include <e32test.h>
+#include <f32file.h>
+#include <bautils.h>
+
+// Globals
+static RFs								TheFs;
+static RTest							TheTest(_L("TLOADER - Test help file loading schema"));
+static CTrapCleanup*					TheTrapCleanup;
+
+// Constants
+
+// Literal constants
+_LIT(KHlpFileSpec,			"*.h*");
+
+_LIT(KHlpFileSearchPath,	"\\Resource\\Help\\");
+
+// Classes referenced
+class CHlpFileEntry;
+
+// Typedefs
+typedef CArrayPtrFlat<CHlpFileEntry> CHlpFileList;
+
+
+//
+// ----> CHlpFileEntry (header)
+//
+
+class CHlpFileEntry : public CBase
+	{
+public:
+	enum THelpFileType
+		{
+		EPrimary,
+		ESecondary,
+		EGeneral,			
+		EInpropperLocale,
+		EDiscarded
+		};
+
+public:
+	static CHlpFileEntry* NewLC(TDriveUnit aDrive, const TDesC& aFile, TLanguage aLanguage);
+
+	inline TDriveUnit Drive() const { return iDrive; }
+	inline const TDesC& FileName() const { return iFile; }
+	inline const TDesC& Name() const { return iName; }
+	inline THelpFileType Type() const { return iType; }
+	inline void SetType(THelpFileType aType) { iType = aType; }
+	inline TLanguage HelpFileLocale() const { return iHelpFileLocale; }
+	void GetFullNameAndPath(TDes& aName) const;
+
+private:
+	CHlpFileEntry(TDriveUnit aDrive, const TDesC& aFile) : iHelpFileLocale(ELangOther), iFile(aFile), iDrive(aDrive) {}
+	void ConstructL(TLanguage aLanguage);
+	void MakeLocaleSpecificExtension(TDes& aNameToAppendTo, TLanguage aLanguage) const;
+	TLanguage NextLanguage(TLanguage aLanguage) const;
+
+private:
+	TLanguage iHelpFileLocale; // Only valid when the type is not EGeneral
+	TName iName;
+	TName iFile;
+	TDriveUnit iDrive;
+	THelpFileType iType;
+	};
+
+
+
+
+//
+// ----> CHlpFileEntry (source)
+//
+
+
+void CHlpFileEntry::ConstructL(TLanguage aLanguage)
+	{
+	_LIT(KDefaultHelpFileExtension, ".hlp");
+
+	// Decide what type of file this is....
+	TFileName file;
+
+	TChar driveLetter;
+	User::LeaveIfError(RFs::DriveToChar(iDrive, driveLetter));
+	file.Append(driveLetter);
+	file.Append(':');
+	file.Append(KHlpFileSearchPath);
+	file.Append(iFile);
+
+	TParsePtrC parser(file);
+	if	(!parser.ExtPresent())
+		User::Leave(KErrCorrupt);
+
+	iName.Copy(parser.Name());
+
+	TPtrC extension(parser.Ext());
+	if	(extension.CompareF(KDefaultHelpFileExtension) == 0)
+		iType = EGeneral;
+	else
+		{
+		TFileName idealHelpFileName(parser.DriveAndPath());
+		idealHelpFileName.Append(parser.Name());
+
+		MakeLocaleSpecificExtension(idealHelpFileName, aLanguage);
+
+		if	(idealHelpFileName.CompareF(file) == 0)
+			{
+			// This is a primary match
+			iType = EPrimary;
+			iHelpFileLocale = aLanguage;
+			}
+		else
+			{
+			// Is it a secondary match? If it isn't then it should be discarded....
+			idealHelpFileName = parser.DriveAndPath();
+			idealHelpFileName.Append(parser.Name());
+	
+			// Get the nearest secondary language
+			aLanguage = NextLanguage(aLanguage);
+			MakeLocaleSpecificExtension(idealHelpFileName, aLanguage);
+
+			if	(idealHelpFileName.CompareF(file) == 0)
+				{
+				iHelpFileLocale = aLanguage;
+				iType = ESecondary;
+				}
+			else
+				{
+				TLex lexer(extension);
+
+				// Skip the leading .H
+				lexer.Inc(2);
+				
+				// Lex the value, but silently ignore errors
+				TUint localeAsUnsignedInt = ELangOther;
+				lexer.Val(localeAsUnsignedInt);
+				iHelpFileLocale = STATIC_CAST(TLanguage, localeAsUnsignedInt);
+				iType = EInpropperLocale;
+				}
+			}
+		}
+	}
+
+
+TLanguage CHlpFileEntry::NextLanguage(TLanguage aLanguage) const
+	{
+	switch(aLanguage)
+		{
+	case ELangSwissFrench:
+		return ELangFrench;
+	case ELangSwissGerman:
+		return ELangGerman;
+	case ELangBelgianFlemish:
+		return ELangDutch;
+	case ELangBelgianFrench:
+		return ELangFrench;
+	default:
+		return ELangEnglish;
+		}
+	}
+
+
+CHlpFileEntry* CHlpFileEntry::NewLC(TDriveUnit aDrive, const TDesC& aFile, TLanguage aLanguage)
+	{
+	CHlpFileEntry* self = new(ELeave) CHlpFileEntry(aDrive, aFile);
+	CleanupStack::PushL(self);
+	self->ConstructL(aLanguage);
+	return self;
+	}
+
+
+//
+//
+//
+
+void CHlpFileEntry::GetFullNameAndPath(TDes& aName) const
+	{
+	TChar driveLetter = '?';
+	RFs::DriveToChar(Drive(), driveLetter);
+	aName.Zero();
+	aName.Append(driveLetter);
+	aName.Append(':');
+	aName.Append(KHlpFileSearchPath);
+	aName.Append(FileName());
+	}
+
+
+void CHlpFileEntry::MakeLocaleSpecificExtension(TDes& aNameToAppendTo, TLanguage aLanguage) const
+	{
+	_LIT(KLocaleExtensionFormat, ".H%02d");
+	aNameToAppendTo.AppendFormat(KLocaleExtensionFormat, aLanguage);
+	}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static void PrintEntryL(const TDesC& aPrompt, const CHlpFileEntry& aEntry, TInt aNumber = -1)
+	{
+	TFileName pFileName(aEntry.FileName());
+	TChar driveLetter = '?';
+	RFs::DriveToChar(aEntry.Drive(), driveLetter);
+
+	//HBufC* buf = HBufC::NewLC(aPrompt.Length() + pFileName.Length() + KHlpFileSearchPath().Length() + 40);
+	//TDes pBuf(buf->Des());
+	TBuf<256> pBuf;		//Tempory fix to allow this to work in Code Warrior builds.
+
+	
+	if	(aNumber >= KErrNone)
+		pBuf.Format(_L("\n%S %d: %c:%S%S\n"), &aPrompt, aNumber, static_cast<TUint>(driveLetter), &KHlpFileSearchPath(), &pFileName);
+	else
+		pBuf.Format(_L("\n%S: %c:%S%S\n"), &aPrompt, static_cast<TUint>(driveLetter), &KHlpFileSearchPath(), &pFileName);
+
+	TheTest.Printf(pBuf);
+
+	//CleanupStack::PopAndDestroy();
+	}
+
+
+// Defines
+#define __PRINT_FILE(aPrompt, aEntry)				(PrintEntryL(aPrompt, aEntry))
+#define __PRINT_FILE_NO(aPrompt, aEntry, aNumber)	(PrintEntryL(aPrompt, aEntry, aNumber))
+
+
+static CHlpFileList* BuildListForDriveLC(TDriveUnit aDrive, RFs& aFsSession)
+//
+//	Generate a list of help files for the specified drive
+//
+	{
+	CHlpFileList* list = new(ELeave) CHlpFileList(5);
+	CleanupStack::PushL(list);
+
+	// Generate the folder spec to search in
+	TFileName searchSpec;
+	TChar driveLetter;
+	User::LeaveIfError(RFs::DriveToChar(aDrive, driveLetter));
+	searchSpec.Append(driveLetter);
+	searchSpec.Append(':');
+	searchSpec.Append(KHlpFileSearchPath);
+	searchSpec.Append(KHlpFileSpec);
+
+	CDir* dirList;
+
+	TLanguage language(User::Language());
+	TFindFile finder(aFsSession);
+	TInt ret = finder.FindWildByPath(searchSpec, NULL, dirList);
+	if	(ret < KErrNone)
+		{
+		if	(ret == KErrNotFound)
+			return list;
+		else
+			User::Leave(ret);
+		}
+	CleanupStack::PushL(dirList);
+
+	// Add files to help file list
+	TInt count = dirList->Count();
+	for(TInt i=0; i<count; i++)
+		{
+		CHlpFileEntry* entry = CHlpFileEntry::NewLC(aDrive, (*dirList)[i].iName, language);
+		__PRINT_FILE_NO(_L("Found entry during dir scan"), *entry, i);
+
+		// At this point we now check to see if there is already a file with the same name
+		// but a different extension already present in the array. If there is already an
+		// entry with a lower 'rating' (e.g. ESecondary) than 'entry' we remove the old one
+		// and add the new, otherwise, we just ignore the new entry as it's not of sufficient
+		// 'quality.'
+		TBool foundMatch = EFalse;
+		
+		TInt existingEntryCount = list->Count();
+		for(TInt j=existingEntryCount-1; j>=0; j--)
+			{
+			CHlpFileEntry* existingEntry = list->At(j);
+			
+			// Is the file already present in the array?
+			if	(existingEntry->Name().CompareF(entry->Name()) == 0)
+				{
+				foundMatch = ETrue;
+				__PRINT_FILE(_L("Entry name duplicate"), *entry);
+
+				// Names are the same but extensions differ in some way...
+				if	(entry->Type() < existingEntry->Type())
+					{
+					__PRINT_FILE(_L("New entry is better than the existing entry with the same name.\n Replacing existing entry with:"), *entry);
+					// The new entry is better than the existing one, so delete the
+					// existing entry and append the new one
+					list->Delete(j);
+					delete existingEntry;
+					list->AppendL(entry);
+					break;
+					}
+				else
+					{
+					// The entry is of lower 'quality' than the existing entries
+					// so we destroy it
+					__PRINT_FILE(_L("Discarding low quality entry"), *entry);
+					CleanupStack::PopAndDestroy(); // entry
+					break;
+					}
+				}
+			}
+
+		// If the file was not present in the existing array then we're free to
+		// add it to the array. Otherwise, we assume that it's been deleted or added
+		// as appropriate by the looping code above.
+		if	(!foundMatch)
+			{
+			list->AppendL(entry);
+			CleanupStack::Pop(); // entry
+			}
+		}
+	CleanupStack::PopAndDestroy(); // dirList
+
+	return list;
+	}
+
+
+static TBool DiskPresent(TInt aDrive)
+	{
+	TDriveList list;
+	if	(TheFs.DriveList(list) < KErrNone)
+		return EFalse;
+
+	TDriveInfo info;
+	TInt error = TheFs.Drive(info, aDrive);
+	if	(error < KErrNone)
+		return EFalse;
+
+	return (list[aDrive] && info.iType != EMediaNotPresent);
+	}
+
+
+static void TestL()
+	{
+	CHlpFileList* masterList = new(ELeave) CHlpFileList(5);
+	CleanupStack::PushL(masterList);
+
+	TInt i = EDriveY;
+	FOREVER
+		{
+		// Make sure we go from Y -> C, then do Z last
+		if	(i < EDriveC)
+			i = EDriveZ;
+		
+		if	(DiskPresent(i))
+			{
+
+			{
+			TChar driveLetter = '?';
+			RFs::DriveToChar(i, driveLetter);
+			TheTest.Printf(_L("\n\nDisk %c: is installed..."), static_cast<TUint>(driveLetter));
+			}
+
+			// This generates a list for a specific directory on the specified drive.
+			CHlpFileList* list = BuildListForDriveLC(TDriveUnit(i), TheFs);
+
+			// We now compare this list with our 'master' list to check for duplicates,
+			// better matches, etc.
+			TInt masterCount = masterList->Count();
+			for(TInt j=masterCount-1; j>=0; j--)
+				{
+				CHlpFileEntry* entry = masterList->At(j);
+
+				// This bit checks to see if an entry in the master list 
+				// collides with an entry in the list we have just generated...
+				//TBool foundCollision = EFalse;
+				TInt newListCount = list->Count();
+				for(TInt k=newListCount-1; k>=0; k--)
+					{
+					CHlpFileEntry* newEntry = list->At(k);
+					__PRINT_FILE_NO(_L("Performing drive resolution on entry"), *newEntry, k);
+
+					if	(entry->Name().CompareF(newEntry->Name()) == 0)
+						{
+						// Names are the same, so compare priorities...
+						if	(newEntry->Type() == entry->Type())
+							{
+							// A file with the same name AND extension is already present in the array,
+							// but are the UID's the same? If they are NOT, then the file
+							// should be treated as a different help file to that of the 
+							// existing array entry.
+							TFileName file;
+
+							TEntry existingTEntry;
+							entry->GetFullNameAndPath(file);
+							User::LeaveIfError(TheFs.Entry(file, existingTEntry));
+
+							TEntry newTEntry;
+							newEntry->GetFullNameAndPath(file);
+							User::LeaveIfError(TheFs.Entry(file, newTEntry));
+
+							if	(existingTEntry.MostDerivedUid() == newTEntry.MostDerivedUid())
+								{
+								// The uids and names of the files are the same. If the extensions are also the same
+								// then we load the file on the drive tending to C:. However, if the extensions differ
+								// we load the file with the extension tending to 01, i.e. UK English.
+								if	(entry->FileName().CompareF(newEntry->FileName()) == 0)
+									{
+									// Name, uid and extensions are the same, therefore load the file on the drive
+									// nearest C:. We do this by setting the priority of the existing entry to
+									// EDsicarded so that it will force the next 'if' statement to fire, and hence
+									// the existing entry will be replaced by the new one.
+									entry->SetType(CHlpFileEntry::EDiscarded);
+									__PRINT_FILE(_L("Uid, name, extension match. Saving entry tending to C: which is"), *newEntry);
+									}
+								else
+									{
+									// Name and uid the same, extensions different therefore load the file with
+									// extension nearest to 01. If the new entry is nearer 01, then we set the
+									// existing entry to discarded and therefore it will be removed at the next
+									// 'if' statement.
+									if	(entry->HelpFileLocale() > newEntry->HelpFileLocale())
+										{
+										entry->SetType(CHlpFileEntry::EDiscarded);
+										__PRINT_FILE(_L("Uid & name match, extensions differ.\n Saving entry tending to H01 which is"), *newEntry);
+										}
+									else
+										{
+										__PRINT_FILE(_L("Uid & name match, extensions differ.\n Discarding new entry because it tends to H99"), *newEntry);
+										}
+									}
+								}
+							else
+								{
+								// This entry should be treated as a separate entity from the current
+								// existing entry because it has different uid's. Therefore, we just
+								// move on to check against the next existing entry in the list
+								__PRINT_FILE(_L("Duplicate name and extension, but different uids detected. New entry == "), *newEntry);
+								continue;
+								}
+							}
+
+						// Is the new entry a better match than the existing one?
+						if	(newEntry->Type() < entry->Type())
+							{
+							// The new entry is of a better 'quality' than the existing
+							// entry, so remove the old and add the new...
+							__PRINT_FILE(_L("Removing stored (new better match found)"), *entry);
+
+							//foundCollision = ETrue;
+							masterList->Delete(j);
+							delete entry;
+							masterList->AppendL(newEntry);
+
+							// Remove it from the new list as the master list now takes
+							// ownership...
+							list->Delete(k);
+							break;
+							}
+						else
+							{
+							// This entry is of lower quality than an existing
+							// entry in the master list, so we can safely discard it.
+							delete newEntry;
+							list->Delete(k);
+							}
+						continue;
+						}
+					}
+				}
+
+			// At this point, anything that is left int the new list should
+			// be valid to add to the master list...
+			TInt newListCount = list->Count();
+			for(TInt k=newListCount-1; k>=0; k--)
+				{
+				__PRINT_FILE_NO(_L("Saving entry"), *list->At(k), k);
+				masterList->AppendL(list->At(k));
+				}
+
+			// We only destroy the list, rather than list+contents because each element
+			// in the new list is now guaranteed to be present in the master list.
+			CleanupStack::PopAndDestroy(); // list
+			}
+
+		if	(i-- == EDriveZ)
+			break;
+		}
+
+
+	// Print out the master list
+	TheTest.Printf(_L("\n\nThe help files that would be loaded are:-"));
+	TheTest.Printf(_L("\n========================================="));
+	TInt masterCount = masterList->Count();
+	for(i=0; i<masterCount; i++)
+		__PRINT_FILE_NO(_L("Entry"), *masterList->At(i), i);
+
+	masterList->ResetAndDestroy();
+	CleanupStack::PopAndDestroy(); // masterList
+	}
+
+/**
+@SYMTestCaseID PIM-TLOADER-0001 
+*/
+
+GLDEF_C TInt E32Main()
+//
+// Test Help file loading
+//
+    {
+	__UHEAP_MARK;
+
+	TheTest.Title();
+	TheTest.Start(_L("@SYMTestCaseID PIM-TLOADER-0001"));
+	TheTest(TheFs.Connect() == KErrNone);
+
+
+	TheTrapCleanup = CTrapCleanup::New();
+	if	(!TheTrapCleanup)
+		return KErrNoMemory;
+
+	TRAPD(r, TestL());
+	TheTest(r == KErrNone);
+
+	delete TheTrapCleanup;
+	TheFs.Close();
+	TheTest.End();
+	TheTest.Close();
+
+	__UHEAP_MARKEND;
+	return KErrNone;
+    }