symhelp/helpmodel/tsrc/TLoader.cpp
changeset 0 1f04cf54edd8
equal deleted inserted replaced
-1:000000000000 0:1f04cf54edd8
       
     1 // Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 // System includes
       
    17 #include <e32test.h>
       
    18 #include <f32file.h>
       
    19 #include <bautils.h>
       
    20 
       
    21 // Globals
       
    22 static RFs								TheFs;
       
    23 static RTest							TheTest(_L("TLOADER - Test help file loading schema"));
       
    24 static CTrapCleanup*					TheTrapCleanup;
       
    25 
       
    26 // Constants
       
    27 
       
    28 // Literal constants
       
    29 _LIT(KHlpFileSpec,			"*.h*");
       
    30 
       
    31 _LIT(KHlpFileSearchPath,	"\\Resource\\Help\\");
       
    32 
       
    33 // Classes referenced
       
    34 class CHlpFileEntry;
       
    35 
       
    36 // Typedefs
       
    37 typedef CArrayPtrFlat<CHlpFileEntry> CHlpFileList;
       
    38 
       
    39 
       
    40 //
       
    41 // ----> CHlpFileEntry (header)
       
    42 //
       
    43 
       
    44 class CHlpFileEntry : public CBase
       
    45 	{
       
    46 public:
       
    47 	enum THelpFileType
       
    48 		{
       
    49 		EPrimary,
       
    50 		ESecondary,
       
    51 		EGeneral,			
       
    52 		EInpropperLocale,
       
    53 		EDiscarded
       
    54 		};
       
    55 
       
    56 public:
       
    57 	static CHlpFileEntry* NewLC(TDriveUnit aDrive, const TDesC& aFile, TLanguage aLanguage);
       
    58 
       
    59 	inline TDriveUnit Drive() const { return iDrive; }
       
    60 	inline const TDesC& FileName() const { return iFile; }
       
    61 	inline const TDesC& Name() const { return iName; }
       
    62 	inline THelpFileType Type() const { return iType; }
       
    63 	inline void SetType(THelpFileType aType) { iType = aType; }
       
    64 	inline TLanguage HelpFileLocale() const { return iHelpFileLocale; }
       
    65 	void GetFullNameAndPath(TDes& aName) const;
       
    66 
       
    67 private:
       
    68 	CHlpFileEntry(TDriveUnit aDrive, const TDesC& aFile) : iHelpFileLocale(ELangOther), iFile(aFile), iDrive(aDrive) {}
       
    69 	void ConstructL(TLanguage aLanguage);
       
    70 	void MakeLocaleSpecificExtension(TDes& aNameToAppendTo, TLanguage aLanguage) const;
       
    71 	TLanguage NextLanguage(TLanguage aLanguage) const;
       
    72 
       
    73 private:
       
    74 	TLanguage iHelpFileLocale; // Only valid when the type is not EGeneral
       
    75 	TName iName;
       
    76 	TName iFile;
       
    77 	TDriveUnit iDrive;
       
    78 	THelpFileType iType;
       
    79 	};
       
    80 
       
    81 
       
    82 
       
    83 
       
    84 //
       
    85 // ----> CHlpFileEntry (source)
       
    86 //
       
    87 
       
    88 
       
    89 void CHlpFileEntry::ConstructL(TLanguage aLanguage)
       
    90 	{
       
    91 	_LIT(KDefaultHelpFileExtension, ".hlp");
       
    92 
       
    93 	// Decide what type of file this is....
       
    94 	TFileName file;
       
    95 
       
    96 	TChar driveLetter;
       
    97 	User::LeaveIfError(RFs::DriveToChar(iDrive, driveLetter));
       
    98 	file.Append(driveLetter);
       
    99 	file.Append(':');
       
   100 	file.Append(KHlpFileSearchPath);
       
   101 	file.Append(iFile);
       
   102 
       
   103 	TParsePtrC parser(file);
       
   104 	if	(!parser.ExtPresent())
       
   105 		User::Leave(KErrCorrupt);
       
   106 
       
   107 	iName.Copy(parser.Name());
       
   108 
       
   109 	TPtrC extension(parser.Ext());
       
   110 	if	(extension.CompareF(KDefaultHelpFileExtension) == 0)
       
   111 		iType = EGeneral;
       
   112 	else
       
   113 		{
       
   114 		TFileName idealHelpFileName(parser.DriveAndPath());
       
   115 		idealHelpFileName.Append(parser.Name());
       
   116 
       
   117 		MakeLocaleSpecificExtension(idealHelpFileName, aLanguage);
       
   118 
       
   119 		if	(idealHelpFileName.CompareF(file) == 0)
       
   120 			{
       
   121 			// This is a primary match
       
   122 			iType = EPrimary;
       
   123 			iHelpFileLocale = aLanguage;
       
   124 			}
       
   125 		else
       
   126 			{
       
   127 			// Is it a secondary match? If it isn't then it should be discarded....
       
   128 			idealHelpFileName = parser.DriveAndPath();
       
   129 			idealHelpFileName.Append(parser.Name());
       
   130 	
       
   131 			// Get the nearest secondary language
       
   132 			aLanguage = NextLanguage(aLanguage);
       
   133 			MakeLocaleSpecificExtension(idealHelpFileName, aLanguage);
       
   134 
       
   135 			if	(idealHelpFileName.CompareF(file) == 0)
       
   136 				{
       
   137 				iHelpFileLocale = aLanguage;
       
   138 				iType = ESecondary;
       
   139 				}
       
   140 			else
       
   141 				{
       
   142 				TLex lexer(extension);
       
   143 
       
   144 				// Skip the leading .H
       
   145 				lexer.Inc(2);
       
   146 				
       
   147 				// Lex the value, but silently ignore errors
       
   148 				TUint localeAsUnsignedInt = ELangOther;
       
   149 				lexer.Val(localeAsUnsignedInt);
       
   150 				iHelpFileLocale = STATIC_CAST(TLanguage, localeAsUnsignedInt);
       
   151 				iType = EInpropperLocale;
       
   152 				}
       
   153 			}
       
   154 		}
       
   155 	}
       
   156 
       
   157 
       
   158 TLanguage CHlpFileEntry::NextLanguage(TLanguage aLanguage) const
       
   159 	{
       
   160 	switch(aLanguage)
       
   161 		{
       
   162 	case ELangSwissFrench:
       
   163 		return ELangFrench;
       
   164 	case ELangSwissGerman:
       
   165 		return ELangGerman;
       
   166 	case ELangBelgianFlemish:
       
   167 		return ELangDutch;
       
   168 	case ELangBelgianFrench:
       
   169 		return ELangFrench;
       
   170 	default:
       
   171 		return ELangEnglish;
       
   172 		}
       
   173 	}
       
   174 
       
   175 
       
   176 CHlpFileEntry* CHlpFileEntry::NewLC(TDriveUnit aDrive, const TDesC& aFile, TLanguage aLanguage)
       
   177 	{
       
   178 	CHlpFileEntry* self = new(ELeave) CHlpFileEntry(aDrive, aFile);
       
   179 	CleanupStack::PushL(self);
       
   180 	self->ConstructL(aLanguage);
       
   181 	return self;
       
   182 	}
       
   183 
       
   184 
       
   185 //
       
   186 //
       
   187 //
       
   188 
       
   189 void CHlpFileEntry::GetFullNameAndPath(TDes& aName) const
       
   190 	{
       
   191 	TChar driveLetter = '?';
       
   192 	RFs::DriveToChar(Drive(), driveLetter);
       
   193 	aName.Zero();
       
   194 	aName.Append(driveLetter);
       
   195 	aName.Append(':');
       
   196 	aName.Append(KHlpFileSearchPath);
       
   197 	aName.Append(FileName());
       
   198 	}
       
   199 
       
   200 
       
   201 void CHlpFileEntry::MakeLocaleSpecificExtension(TDes& aNameToAppendTo, TLanguage aLanguage) const
       
   202 	{
       
   203 	_LIT(KLocaleExtensionFormat, ".H%02d");
       
   204 	aNameToAppendTo.AppendFormat(KLocaleExtensionFormat, aLanguage);
       
   205 	}
       
   206 
       
   207 
       
   208 
       
   209 
       
   210 
       
   211 
       
   212 
       
   213 
       
   214 
       
   215 
       
   216 
       
   217 
       
   218 
       
   219 
       
   220 
       
   221 
       
   222 static void PrintEntryL(const TDesC& aPrompt, const CHlpFileEntry& aEntry, TInt aNumber = -1)
       
   223 	{
       
   224 	TFileName pFileName(aEntry.FileName());
       
   225 	TChar driveLetter = '?';
       
   226 	RFs::DriveToChar(aEntry.Drive(), driveLetter);
       
   227 
       
   228 	//HBufC* buf = HBufC::NewLC(aPrompt.Length() + pFileName.Length() + KHlpFileSearchPath().Length() + 40);
       
   229 	//TDes pBuf(buf->Des());
       
   230 	TBuf<256> pBuf;		//Tempory fix to allow this to work in Code Warrior builds.
       
   231 
       
   232 	
       
   233 	if	(aNumber >= KErrNone)
       
   234 		pBuf.Format(_L("\n%S %d: %c:%S%S\n"), &aPrompt, aNumber, static_cast<TUint>(driveLetter), &KHlpFileSearchPath(), &pFileName);
       
   235 	else
       
   236 		pBuf.Format(_L("\n%S: %c:%S%S\n"), &aPrompt, static_cast<TUint>(driveLetter), &KHlpFileSearchPath(), &pFileName);
       
   237 
       
   238 	TheTest.Printf(pBuf);
       
   239 
       
   240 	//CleanupStack::PopAndDestroy();
       
   241 	}
       
   242 
       
   243 
       
   244 // Defines
       
   245 #define __PRINT_FILE(aPrompt, aEntry)				(PrintEntryL(aPrompt, aEntry))
       
   246 #define __PRINT_FILE_NO(aPrompt, aEntry, aNumber)	(PrintEntryL(aPrompt, aEntry, aNumber))
       
   247 
       
   248 
       
   249 static CHlpFileList* BuildListForDriveLC(TDriveUnit aDrive, RFs& aFsSession)
       
   250 //
       
   251 //	Generate a list of help files for the specified drive
       
   252 //
       
   253 	{
       
   254 	CHlpFileList* list = new(ELeave) CHlpFileList(5);
       
   255 	CleanupStack::PushL(list);
       
   256 
       
   257 	// Generate the folder spec to search in
       
   258 	TFileName searchSpec;
       
   259 	TChar driveLetter;
       
   260 	User::LeaveIfError(RFs::DriveToChar(aDrive, driveLetter));
       
   261 	searchSpec.Append(driveLetter);
       
   262 	searchSpec.Append(':');
       
   263 	searchSpec.Append(KHlpFileSearchPath);
       
   264 	searchSpec.Append(KHlpFileSpec);
       
   265 
       
   266 	CDir* dirList;
       
   267 
       
   268 	TLanguage language(User::Language());
       
   269 	TFindFile finder(aFsSession);
       
   270 	TInt ret = finder.FindWildByPath(searchSpec, NULL, dirList);
       
   271 	if	(ret < KErrNone)
       
   272 		{
       
   273 		if	(ret == KErrNotFound)
       
   274 			return list;
       
   275 		else
       
   276 			User::Leave(ret);
       
   277 		}
       
   278 	CleanupStack::PushL(dirList);
       
   279 
       
   280 	// Add files to help file list
       
   281 	TInt count = dirList->Count();
       
   282 	for(TInt i=0; i<count; i++)
       
   283 		{
       
   284 		CHlpFileEntry* entry = CHlpFileEntry::NewLC(aDrive, (*dirList)[i].iName, language);
       
   285 		__PRINT_FILE_NO(_L("Found entry during dir scan"), *entry, i);
       
   286 
       
   287 		// At this point we now check to see if there is already a file with the same name
       
   288 		// but a different extension already present in the array. If there is already an
       
   289 		// entry with a lower 'rating' (e.g. ESecondary) than 'entry' we remove the old one
       
   290 		// and add the new, otherwise, we just ignore the new entry as it's not of sufficient
       
   291 		// 'quality.'
       
   292 		TBool foundMatch = EFalse;
       
   293 		
       
   294 		TInt existingEntryCount = list->Count();
       
   295 		for(TInt j=existingEntryCount-1; j>=0; j--)
       
   296 			{
       
   297 			CHlpFileEntry* existingEntry = list->At(j);
       
   298 			
       
   299 			// Is the file already present in the array?
       
   300 			if	(existingEntry->Name().CompareF(entry->Name()) == 0)
       
   301 				{
       
   302 				foundMatch = ETrue;
       
   303 				__PRINT_FILE(_L("Entry name duplicate"), *entry);
       
   304 
       
   305 				// Names are the same but extensions differ in some way...
       
   306 				if	(entry->Type() < existingEntry->Type())
       
   307 					{
       
   308 					__PRINT_FILE(_L("New entry is better than the existing entry with the same name.\n Replacing existing entry with:"), *entry);
       
   309 					// The new entry is better than the existing one, so delete the
       
   310 					// existing entry and append the new one
       
   311 					list->Delete(j);
       
   312 					delete existingEntry;
       
   313 					list->AppendL(entry);
       
   314 					break;
       
   315 					}
       
   316 				else
       
   317 					{
       
   318 					// The entry is of lower 'quality' than the existing entries
       
   319 					// so we destroy it
       
   320 					__PRINT_FILE(_L("Discarding low quality entry"), *entry);
       
   321 					CleanupStack::PopAndDestroy(); // entry
       
   322 					break;
       
   323 					}
       
   324 				}
       
   325 			}
       
   326 
       
   327 		// If the file was not present in the existing array then we're free to
       
   328 		// add it to the array. Otherwise, we assume that it's been deleted or added
       
   329 		// as appropriate by the looping code above.
       
   330 		if	(!foundMatch)
       
   331 			{
       
   332 			list->AppendL(entry);
       
   333 			CleanupStack::Pop(); // entry
       
   334 			}
       
   335 		}
       
   336 	CleanupStack::PopAndDestroy(); // dirList
       
   337 
       
   338 	return list;
       
   339 	}
       
   340 
       
   341 
       
   342 static TBool DiskPresent(TInt aDrive)
       
   343 	{
       
   344 	TDriveList list;
       
   345 	if	(TheFs.DriveList(list) < KErrNone)
       
   346 		return EFalse;
       
   347 
       
   348 	TDriveInfo info;
       
   349 	TInt error = TheFs.Drive(info, aDrive);
       
   350 	if	(error < KErrNone)
       
   351 		return EFalse;
       
   352 
       
   353 	return (list[aDrive] && info.iType != EMediaNotPresent);
       
   354 	}
       
   355 
       
   356 
       
   357 static void TestL()
       
   358 	{
       
   359 	CHlpFileList* masterList = new(ELeave) CHlpFileList(5);
       
   360 	CleanupStack::PushL(masterList);
       
   361 
       
   362 	TInt i = EDriveY;
       
   363 	FOREVER
       
   364 		{
       
   365 		// Make sure we go from Y -> C, then do Z last
       
   366 		if	(i < EDriveC)
       
   367 			i = EDriveZ;
       
   368 		
       
   369 		if	(DiskPresent(i))
       
   370 			{
       
   371 
       
   372 			{
       
   373 			TChar driveLetter = '?';
       
   374 			RFs::DriveToChar(i, driveLetter);
       
   375 			TheTest.Printf(_L("\n\nDisk %c: is installed..."), static_cast<TUint>(driveLetter));
       
   376 			}
       
   377 
       
   378 			// This generates a list for a specific directory on the specified drive.
       
   379 			CHlpFileList* list = BuildListForDriveLC(TDriveUnit(i), TheFs);
       
   380 
       
   381 			// We now compare this list with our 'master' list to check for duplicates,
       
   382 			// better matches, etc.
       
   383 			TInt masterCount = masterList->Count();
       
   384 			for(TInt j=masterCount-1; j>=0; j--)
       
   385 				{
       
   386 				CHlpFileEntry* entry = masterList->At(j);
       
   387 
       
   388 				// This bit checks to see if an entry in the master list 
       
   389 				// collides with an entry in the list we have just generated...
       
   390 				//TBool foundCollision = EFalse;
       
   391 				TInt newListCount = list->Count();
       
   392 				for(TInt k=newListCount-1; k>=0; k--)
       
   393 					{
       
   394 					CHlpFileEntry* newEntry = list->At(k);
       
   395 					__PRINT_FILE_NO(_L("Performing drive resolution on entry"), *newEntry, k);
       
   396 
       
   397 					if	(entry->Name().CompareF(newEntry->Name()) == 0)
       
   398 						{
       
   399 						// Names are the same, so compare priorities...
       
   400 						if	(newEntry->Type() == entry->Type())
       
   401 							{
       
   402 							// A file with the same name AND extension is already present in the array,
       
   403 							// but are the UID's the same? If they are NOT, then the file
       
   404 							// should be treated as a different help file to that of the 
       
   405 							// existing array entry.
       
   406 							TFileName file;
       
   407 
       
   408 							TEntry existingTEntry;
       
   409 							entry->GetFullNameAndPath(file);
       
   410 							User::LeaveIfError(TheFs.Entry(file, existingTEntry));
       
   411 
       
   412 							TEntry newTEntry;
       
   413 							newEntry->GetFullNameAndPath(file);
       
   414 							User::LeaveIfError(TheFs.Entry(file, newTEntry));
       
   415 
       
   416 							if	(existingTEntry.MostDerivedUid() == newTEntry.MostDerivedUid())
       
   417 								{
       
   418 								// The uids and names of the files are the same. If the extensions are also the same
       
   419 								// then we load the file on the drive tending to C:. However, if the extensions differ
       
   420 								// we load the file with the extension tending to 01, i.e. UK English.
       
   421 								if	(entry->FileName().CompareF(newEntry->FileName()) == 0)
       
   422 									{
       
   423 									// Name, uid and extensions are the same, therefore load the file on the drive
       
   424 									// nearest C:. We do this by setting the priority of the existing entry to
       
   425 									// EDsicarded so that it will force the next 'if' statement to fire, and hence
       
   426 									// the existing entry will be replaced by the new one.
       
   427 									entry->SetType(CHlpFileEntry::EDiscarded);
       
   428 									__PRINT_FILE(_L("Uid, name, extension match. Saving entry tending to C: which is"), *newEntry);
       
   429 									}
       
   430 								else
       
   431 									{
       
   432 									// Name and uid the same, extensions different therefore load the file with
       
   433 									// extension nearest to 01. If the new entry is nearer 01, then we set the
       
   434 									// existing entry to discarded and therefore it will be removed at the next
       
   435 									// 'if' statement.
       
   436 									if	(entry->HelpFileLocale() > newEntry->HelpFileLocale())
       
   437 										{
       
   438 										entry->SetType(CHlpFileEntry::EDiscarded);
       
   439 										__PRINT_FILE(_L("Uid & name match, extensions differ.\n Saving entry tending to H01 which is"), *newEntry);
       
   440 										}
       
   441 									else
       
   442 										{
       
   443 										__PRINT_FILE(_L("Uid & name match, extensions differ.\n Discarding new entry because it tends to H99"), *newEntry);
       
   444 										}
       
   445 									}
       
   446 								}
       
   447 							else
       
   448 								{
       
   449 								// This entry should be treated as a separate entity from the current
       
   450 								// existing entry because it has different uid's. Therefore, we just
       
   451 								// move on to check against the next existing entry in the list
       
   452 								__PRINT_FILE(_L("Duplicate name and extension, but different uids detected. New entry == "), *newEntry);
       
   453 								continue;
       
   454 								}
       
   455 							}
       
   456 
       
   457 						// Is the new entry a better match than the existing one?
       
   458 						if	(newEntry->Type() < entry->Type())
       
   459 							{
       
   460 							// The new entry is of a better 'quality' than the existing
       
   461 							// entry, so remove the old and add the new...
       
   462 							__PRINT_FILE(_L("Removing stored (new better match found)"), *entry);
       
   463 
       
   464 							//foundCollision = ETrue;
       
   465 							masterList->Delete(j);
       
   466 							delete entry;
       
   467 							masterList->AppendL(newEntry);
       
   468 
       
   469 							// Remove it from the new list as the master list now takes
       
   470 							// ownership...
       
   471 							list->Delete(k);
       
   472 							break;
       
   473 							}
       
   474 						else
       
   475 							{
       
   476 							// This entry is of lower quality than an existing
       
   477 							// entry in the master list, so we can safely discard it.
       
   478 							delete newEntry;
       
   479 							list->Delete(k);
       
   480 							}
       
   481 						continue;
       
   482 						}
       
   483 					}
       
   484 				}
       
   485 
       
   486 			// At this point, anything that is left int the new list should
       
   487 			// be valid to add to the master list...
       
   488 			TInt newListCount = list->Count();
       
   489 			for(TInt k=newListCount-1; k>=0; k--)
       
   490 				{
       
   491 				__PRINT_FILE_NO(_L("Saving entry"), *list->At(k), k);
       
   492 				masterList->AppendL(list->At(k));
       
   493 				}
       
   494 
       
   495 			// We only destroy the list, rather than list+contents because each element
       
   496 			// in the new list is now guaranteed to be present in the master list.
       
   497 			CleanupStack::PopAndDestroy(); // list
       
   498 			}
       
   499 
       
   500 		if	(i-- == EDriveZ)
       
   501 			break;
       
   502 		}
       
   503 
       
   504 
       
   505 	// Print out the master list
       
   506 	TheTest.Printf(_L("\n\nThe help files that would be loaded are:-"));
       
   507 	TheTest.Printf(_L("\n========================================="));
       
   508 	TInt masterCount = masterList->Count();
       
   509 	for(i=0; i<masterCount; i++)
       
   510 		__PRINT_FILE_NO(_L("Entry"), *masterList->At(i), i);
       
   511 
       
   512 	masterList->ResetAndDestroy();
       
   513 	CleanupStack::PopAndDestroy(); // masterList
       
   514 	}
       
   515 
       
   516 /**
       
   517 @SYMTestCaseID PIM-TLOADER-0001 
       
   518 */
       
   519 
       
   520 GLDEF_C TInt E32Main()
       
   521 //
       
   522 // Test Help file loading
       
   523 //
       
   524     {
       
   525 	__UHEAP_MARK;
       
   526 
       
   527 	TheTest.Title();
       
   528 	TheTest.Start(_L("@SYMTestCaseID PIM-TLOADER-0001"));
       
   529 	TheTest(TheFs.Connect() == KErrNone);
       
   530 
       
   531 
       
   532 	TheTrapCleanup = CTrapCleanup::New();
       
   533 	if	(!TheTrapCleanup)
       
   534 		return KErrNoMemory;
       
   535 
       
   536 	TRAPD(r, TestL());
       
   537 	TheTest(r == KErrNone);
       
   538 
       
   539 	delete TheTrapCleanup;
       
   540 	TheFs.Close();
       
   541 	TheTest.End();
       
   542 	TheTest.Close();
       
   543 
       
   544 	__UHEAP_MARKEND;
       
   545 	return KErrNone;
       
   546     }