|
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 } |