diff -r 000000000000 -r 1f04cf54edd8 symhelp/helpmodel/tsrc/TLoader.cpp --- /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 +#include +#include + +// 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 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(driveLetter), &KHlpFileSearchPath(), &pFileName); + else + pBuf.Format(_L("\n%S: %c:%S%S\n"), &aPrompt, static_cast(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; iCount(); + 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(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; iAt(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; + }