--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/textandlocutils/nearestlangutils/src/LangUtil.cpp Fri Sep 17 08:40:09 2010 +0300
@@ -0,0 +1,908 @@
+// 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 "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:
+//
+
+#include <bafl/langutil.h>
+#include <hal.h>
+#include <hal_data.h>
+#include "LangUtilImpl.h"
+
+
+/**
+Mimimum length of a filename and mimimum length of a suffix.
+Note these two values are tied together.
+*/
+const TInt KInvNameAndMinSuffixLength = 2;
+
+#define ISDIGIT(c) (c-'0' >= 0 && c-'0' <= 9)
+
+_LIT(KAllDrives, "YXWVUTSRQPONMLKJIHGFEDCBAZ");
+
+LOCAL_C const TLanguage dp0[] = { ELangCanadianEnglish, ELangAmerican,ELangEnglish, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangAustralian,ELangNewZealand,ELangInternationalEnglish,ELangSouthAfricanEnglish,ELangNone };
+LOCAL_C const TLanguage dp1[] = { ELangAmerican, ELangEnglish,ELangCanadianEnglish, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangAustralian,ELangNewZealand,ELangInternationalEnglish,ELangSouthAfricanEnglish, ELangNone };
+LOCAL_C const TLanguage dp2[] = { ELangAustralian, ELangEnglish, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangInternationalEnglish,ELangSouthAfricanEnglish, ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp3[] = { ELangSouthAfricanEnglish, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangInternationalEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp4[] = { ELangInternationalEnglish, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp5[] = { ELangEnglish_Apac, ELangEnglish, ELangAustralian, ELangAmerican,ELangInternationalEnglish,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp6[] = { ELangEnglish_Taiwan, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangInternationalEnglish,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp7[] = { ELangEnglish_HongKong, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangInternationalEnglish,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp8[] = { ELangEnglish_Prc, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangInternationalEnglish,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp9[] = { ELangEnglish_Japan, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangInternationalEnglish,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp10[] = { ELangEnglish_Thailand, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangInternationalEnglish,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp11[] = { ELangEnglish_India, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangInternationalEnglish,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp12[] = { ELangNewZealand, ELangEnglish, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangAustralian,ELangInternationalEnglish,ELangSouthAfricanEnglish, ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp13[] = { ELangInternationalFrench,ELangFrench,ELangSwissFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone };
+LOCAL_C const TLanguage dp14[] = { ELangBelgianFrench, ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangCanadianFrench,ELangNone };
+LOCAL_C const TLanguage dp15[] = { ELangCanadianFrench, ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangBelgianFrench,ELangNone };
+LOCAL_C const TLanguage dp16[] = { ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone };
+LOCAL_C const TLanguage dp17[] = { ELangSwissFrench,ELangFrench,ELangInternationalFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone };
+LOCAL_C const TLanguage dp18[] = { ELangSwissGerman,ELangGerman,ELangAustrian,ELangNone };
+LOCAL_C const TLanguage dp19[] = { ELangAustrian,ELangGerman,ELangSwissGerman,ELangNone };
+LOCAL_C const TLanguage dp20[] = { ELangGerman,ELangSwissGerman,ELangAustrian,ELangNone };
+LOCAL_C const TLanguage dp21[] = { ELangSerbian,ELangCroatian,ELangNone };
+LOCAL_C const TLanguage dp22[] = { ELangCroatian,ELangSerbian,ELangNone };
+LOCAL_C const TLanguage dp23[] = { ELangRomanian,ELangMoldavian,ELangNone };
+LOCAL_C const TLanguage dp24[] = { ELangMoldavian,ELangRomanian,ELangNone };
+LOCAL_C const TLanguage dp25[] = { ELangBelgianFlemish,ELangDutch,ELangNone };
+LOCAL_C const TLanguage dp26[] = { ELangDutch,ELangBelgianFlemish,ELangNone };
+LOCAL_C const TLanguage dp27[] = { ELangAfrikaans,ELangDutch,ELangNone };
+LOCAL_C const TLanguage dp28[] = { ELangMalay_Apac,ELangMalay,ELangNone };
+LOCAL_C const TLanguage dp29[] = { ELangIndonesian_Apac,ELangIndonesian,ELangNone };
+LOCAL_C const TLanguage dp30[] = { ELangSpanish,ELangInternationalSpanish,ELangLatinAmericanSpanish,ELangNone };
+LOCAL_C const TLanguage dp31[] = { ELangLatinAmericanSpanish,ELangSpanish,ELangInternationalSpanish,ELangNone };
+LOCAL_C const TLanguage dp32[] = { ELangInternationalSpanish,ELangSpanish,ELangLatinAmericanSpanish,ELangNone };
+LOCAL_C const TLanguage dp33[] = { ELangCyprusGreek,ELangGreek,ELangNone };
+LOCAL_C const TLanguage dp34[] = { ELangGreek,ELangCyprusGreek,ELangNone };
+LOCAL_C const TLanguage dp35[] = { ELangSwissItalian,ELangItalian,ELangNone };
+LOCAL_C const TLanguage dp36[] = { ELangItalian,ELangSwissItalian,ELangNone };
+LOCAL_C const TLanguage dp37[] = { ELangBrazilianPortuguese,ELangPortuguese,ELangNone };
+LOCAL_C const TLanguage dp38[] = { ELangPortuguese,ELangBrazilianPortuguese,ELangNone };
+LOCAL_C const TLanguage dp39[] = { ELangFinlandSwedish,ELangSwedish,ELangNone };
+LOCAL_C const TLanguage dp40[] = { ELangSwedish,ELangFinlandSwedish,ELangNone };
+LOCAL_C const TLanguage dp41[] = { ELangCyprusTurkish,ELangTurkish,ELangNone };
+LOCAL_C const TLanguage dp42[] = { ELangTurkish,ELangCyprusTurkish,ELangNone };
+LOCAL_C const TLanguage dp43[] = { ELangHongKongChinese, ELangTaiwanChinese, ELangPrcChinese,ELangNone };
+LOCAL_C const TLanguage dp44[] = { ELangTaiwanChinese, ELangHongKongChinese,ELangPrcChinese,ELangNone };
+LOCAL_C const TLanguage dp45[] = { ELangPrcChinese, ELangHongKongChinese, ELangTaiwanChinese,ELangNone };
+LOCAL_C const TLanguage * const KEquivalentLists[] = { dp0, dp1, dp2, dp3, dp4, dp5, dp6,
+ dp7, dp8, dp9, dp10, dp11, dp12, dp13, dp14, dp15, dp16, dp17,
+ dp18, dp19, dp20, dp21, dp22, dp23, dp24, dp25, dp26, dp27,
+ dp28, dp29, dp30, dp31, dp32, dp33, dp34, dp35, dp36, dp37,
+ dp38, dp39, dp40, dp41, dp42, dp43, dp44, dp45};
+
+
+
+LOCAL_C TBool IsLanguageExtended(const TLanguage aLanguage)
+ {
+ // For compatibility reasons, ELangNone is 0xFFFF. However, it's not an extended language.
+ if ((aLanguage==ELangNone) || ((static_cast<TUint>(aLanguage))<=KDialectMask))
+ return EFalse;
+ else
+ return ETrue;
+ }
+
+
+LOCAL_C TLanguage BaseLanguage(const TLanguage aLanguage)
+ {
+ if (IsLanguageExtended(aLanguage))
+ return static_cast<TLanguage>(aLanguage & KDialectMask);
+ else
+ return aLanguage;
+ }
+
+LOCAL_C TLanguage NextLanguage(TLanguage aLanguage)
+/** Returns the next best language to use after aLanguage,
+based on Symbian's base table of language near-equivalence.
+@internalAll */
+ {
+ switch (aLanguage)
+ {
+ case ELangAustralian:
+ case ELangNewZealand:
+ case ELangSouthAfricanEnglish:
+ case ELangInternationalEnglish:
+ case ELangAmerican:
+ case ELangEnglish_Apac:
+ case ELangEnglish_Taiwan:
+ case ELangEnglish_HongKong:
+ case ELangEnglish_Prc:
+ case ELangEnglish_Japan:
+ case ELangEnglish_Thailand:
+ return ELangEnglish;
+ case ELangCanadianEnglish:
+ return ELangAmerican; // 2-stage downgrade
+ case ELangSwissFrench:
+ case ELangBelgianFrench:
+ case ELangInternationalFrench:
+ case ELangCanadianFrench:
+ return ELangFrench;
+ case ELangSwissGerman:
+ case ELangAustrian:
+ return ELangGerman;
+ case ELangInternationalSpanish:
+ case ELangLatinAmericanSpanish:
+ return ELangSpanish;
+ case ELangSwissItalian:
+ return ELangItalian;
+ case ELangFinlandSwedish:
+ return ELangSwedish;
+ case ELangCyprusTurkish:
+ return ELangTurkish;
+ case ELangBelgianFlemish:
+ return ELangDutch;
+ case ELangHongKongChinese:
+ return ELangTaiwanChinese;
+ case ELangCyprusGreek:
+ return ELangGreek;
+ case ELangMalay_Apac:
+ return ELangMalay;
+ case ELangBrazilianPortuguese:
+ return ELangPortuguese;
+ default:
+ return ELangNone;
+ }
+ }
+
+
+void AddLanguage(TLanguagePath& aPath, TLanguage aNewLanguage)
+/** Add language to the language path if there is space.
+The first empty slot must have "ELangNone" in it. This will also be true
+on exit. */
+ {
+ TLanguage *p = aPath;
+ const TLanguage *end = &(aPath[KMaxDowngradeLanguages]);
+ while (p != end)
+ {
+ if (*p == aNewLanguage)
+ // language already in list
+ break;
+ if (*p == ELangNone)
+ {
+ // found the end of the list
+ p[0] = aNewLanguage;
+ p[1] = ELangNone;
+ break;
+ }
+ ++p;
+ }
+ return;
+ }
+
+void MakeLanguageDowngradePath(TLanguagePath& aPath,
+ TLanguage aCurrent, TLanguage aIdeal, const TLocale& aLocale)
+ {
+ TInt j = 0;
+ if( aIdeal != ELangNone)
+ {
+ aPath[j++]=aIdeal;
+ }
+ aPath[j++] = aCurrent;
+ aPath[j++] = ELangNone;
+
+ if (aCurrent & ~KDialectMask)
+ AddLanguage(aPath, static_cast<TLanguage>(aCurrent & KDialectMask));
+
+ for (TInt i=0;i<=2;i++)
+ {
+ AddLanguage(aPath, aLocale.LanguageDowngrade(i));
+ AddLanguage(aPath, BaseLanguage(aLocale.LanguageDowngrade(i)));
+ }
+
+ while (ELangNone != (aCurrent = NextLanguage(BaseLanguage(aCurrent))))
+ AddLanguage(aPath, aCurrent);
+ }
+
+
+
+//EXPORT_C
+void LangUtil::GetDowngradePathL(const RFs& aFs, const TLanguage aCurrentLanguage, RArray<TLanguage>& aLanguageArray){
+
+ TLocale currentLocale;
+ TNearestLanguageFileFinder languageDowngradePath(aFs);
+ TLanguage idealLanguage=IdealLanguage();
+ MakeLanguageDowngradePath(languageDowngradePath.iPath,aCurrentLanguage,idealLanguage, currentLocale);
+ aLanguageArray.Reset();
+ const TLanguage* p=languageDowngradePath.iPath;
+ while (*p != ELangNone)
+ {
+ User::LeaveIfError(aLanguageArray.Append(*p));
+ ++p;
+ }
+
+}
+
+
+//EXPORT_C
+void LangUtil::GetEquivalentLanguageList(TLanguage aLang, TLanguagePath& aEquivalents){
+
+ aEquivalents[0] = aLang;
+ aEquivalents[1] = ELangNone;
+ const TInt len = sizeof(KEquivalentLists) / sizeof(KEquivalentLists[0]);
+ for (TInt i = 0; i < len; ++i)
+ {
+ const TLanguage *ptr = KEquivalentLists[i];
+ if (ptr[0] == aLang)
+ {
+ TInt index = 1;
+ while (ELangNone != *ptr)
+ {
+ aEquivalents[index++] = (TLanguage)*(++ptr);
+ }
+ aEquivalents[index] = ELangNone;
+ break;
+ } // end if ptr[0]
+ } // end for i
+
+}
+
+
+
+//EXPORT_C
+TLanguage LangUtil::IdealLanguage(){
+
+ TLanguage* langPtr=(TLanguage*)Dll::Tls();
+
+ if( langPtr==NULL)
+ {
+ return(ELangNone);
+ }
+
+ return(*langPtr);
+
+}
+
+
+//EXPORT_C
+void LangUtil::NearestLanguageFile(const RFs& aFs, TFileName& aName){
+
+ TLanguage language;
+
+ NearestLanguageFile( aFs, aName, language);
+
+ (void)language;
+
+}
+
+
+//EXPORT_C
+void LangUtil::NearestLanguageFile(const RFs& aFs, TFileName& aName, TLanguage& aLanguage){
+#if defined(DO_PROFILING)
+ RDebug::ProfileReset(FIRST_PROFILE_INDEX, PROFILE_COUNT);
+ RDebug::ProfileStart(PROFILE_INDEX_1);
+#endif
+ TNearestLanguageFileFinder finder(aFs);
+ TBool goodSuffix=finder.SetFileName(aName);
+
+ // Only continue if the suffix is good.
+ if(goodSuffix)
+ {
+ // add preset customised resource drive to drive list
+ // Note that errors returned from AddCustomResourceDrive are ignored. This is because if
+ // a custom resource drive has not been found we still want to continue on with searching
+ // other drives according to our algorithm
+ finder.AddCustomResourceDrive();
+
+ TLocale locale;
+ TLanguage idealLanguage;
+ idealLanguage = IdealLanguage();
+ MakeLanguageDowngradePath(finder.iPath, User::Language(), idealLanguage, locale);
+ if (!finder.FindLanguageAndDrive()
+ && KErrNone != finder.FindFirstLanguageFileAndDrive())
+ finder.RepairFileName();
+ aLanguage = finder.Language();
+ }
+
+#if defined(DO_PROFILING)
+ RDebug::ProfileEnd(PROFILE_INDEX_1);
+ TProfile profile[PROFILE_COUNT];
+ RDebug::ProfileResult(&profile[0], FIRST_PROFILE_INDEX, PROFILE_COUNT);
+ if(goodSuffix)
+ {
+ RDebug::Print(_L("BaflUtils::NearestLanguageFile profile info: %d.%03ds"), profile[PROFILE_INDEX_1-FIRST_PROFILE_INDEX].iTime/1000000, profile[PROFILE_INDEX_1-FIRST_PROFILE_INDEX].iTime%1000000);
+ }
+ else
+ {
+ RDebug::Print(_L("BaflUtils::NearestLanguageFile (bad suffix ) profile info: %d.%03ds"), profile[PROFILE_INDEX_1-FIRST_PROFILE_INDEX].iTime/1000000, profile[PROFILE_INDEX_1-FIRST_PROFILE_INDEX].iTime%1000000);
+ }
+#endif
+
+}
+
+
+//EXPORT_C
+void LangUtil::NearestLanguageFileV2(const RFs& aFs, TFileName& aName, TLanguage& aLanguage){
+
+ TNearestLanguageFileFinder finder(aFs);
+ TBool goodSuffix=finder.SetFileName(aName);
+
+ // Continue only if the suffix is good.
+ if(goodSuffix)
+ {
+ // add preset customised resource drive to drive list
+ // Note that errors returned from AddCustomResourceDrive are ignored. This is because if
+ // a custom resource drive has not been found we still want to continue on with searching
+ // other drives according to our algorithm
+ finder.AddCustomResourceDrive();
+
+ GetEquivalentLanguageList(User::Language(), finder.iPath);
+ if (!finder.FindLanguageAndDrive()
+ && KErrNone != finder.FindFirstLanguageFileAndDrive())
+ finder.RepairFileName();
+ aLanguage = finder.Language();
+ }
+ else
+ {
+ aLanguage = ELangNone;
+ }
+
+}
+
+
+//EXPORT_C
+void LangUtil::ReleaseIdealLanguage(){
+
+ TLanguage* aLanguage=(TLanguage*)Dll::Tls();
+ if( aLanguage==NULL)
+ return;
+
+ delete aLanguage;
+ Dll::FreeTls();
+
+}
+
+
+//EXPORT_C
+TInt LangUtil::SetIdealLanguage(TLanguage aLanguage){
+TLanguage* langPtr=(TLanguage*)Dll::Tls();
+ if( langPtr==NULL)
+ {
+ langPtr=(TLanguage*)User::Alloc(sizeof(TLanguage));
+
+ if(!langPtr)
+ return(KErrNoMemory);
+
+ TInt ret=Dll::SetTls(langPtr);
+
+ if(ret!=KErrNone)
+ return(ret);
+ }
+ *langPtr=aLanguage;
+ return(KErrNone);
+
+}
+
+TInt RRealDirectoryScanner::Open(RFs& aFs, const TDesC& aMatchPattern)
+ {
+ return iDir.Open(aFs, aMatchPattern,
+ KEntryAttReadOnly | KEntryAttHidden | KEntryAttSystem | KEntryAttArchive);
+ }
+
+TInt RRealDirectoryScanner::Next(TEntry& aOut)
+ {
+ return iDir.Read(aOut);
+ }
+
+void RRealDirectoryScanner::Close()
+ {
+ iDir.Close();
+ }
+
+/**
+Simply counts the number of numerical characters at the end of the name passed.
+
+@internalComponent
+@param aFilename The filename to parse
+
+@return Count of the numeric digits at the end of the name passed,
+ e.g. x.r491 gives 3.
+*/
+TInt TNearestLanguageFileFinder::CountDigitsFromEnd(const TDesC& aFilename)
+ {
+ TInt digitCount = 0;
+
+ for (TInt idx = aFilename.Length()-1; idx>=0 && ISDIGIT (aFilename [idx]); --idx)
+ {
+ ++digitCount;
+ }
+
+ return digitCount;
+ }
+
+
+/**
+Counts the number of digits at the end of a filename.
+
+@internalComponent
+@param aFilename The filename to parse
+
+@return Count of the numeric digits at the end of the suffix,
+ e.g. x.r491 gives 3.
+ 0 if no numeric end of suffix,
+ KErrBadName for an invalid filename,
+ KErrNotSupported if the filename (minus path) is less
+ than or equal to KInvNameAndMinSuffixLength in length
+*/
+TInt TNearestLanguageFileFinder::CountDigitsFromEndInSuffix (const TDesC& aFilename)
+ {
+ TInt digitCount = 0;
+ TInt slashIdx = 0;
+ TInt len = aFilename.Length ();
+
+ // NOTE: We didn't use TChar here as they are too slow.
+ // We also didn't use TParse as they are too large.
+
+ // don't work on the path
+ for (slashIdx=len-1; slashIdx >= 0 && aFilename[slashIdx] != '\\'; --slashIdx)
+ {/*do nothing*/};
+
+ // Get new length
+ if (slashIdx>=0) {len = len-slashIdx-1;}
+
+ // Initial test to see if filename legal size.
+ if (len > KInvNameAndMinSuffixLength)
+ {
+ digitCount = CountDigitsFromEnd(aFilename);
+
+ // Can't store something bigger or we'll panic!
+ if (digitCount > KMaxSuffixLength)
+ {
+ digitCount = KErrBadName;
+ }
+ else
+ // numeric filename, e.g. "1234".
+ // No preceeding alpha character
+ if (!(len-digitCount))
+ {
+ digitCount = KErrBadName;
+ }
+ }
+ else
+ {
+ digitCount = KErrNotSupported;
+ }
+
+ return digitCount;
+ }
+
+RDirectoryScanner& TNearestLanguageFileFinder::DirectoryScanner()
+ {
+ return iDirScanner;
+ }
+
+TBool TNearestLanguageFileFinder::FileExists(const TDesC& aFileName) const
+ {
+ //return BaflUtils::FileExists(iFs, aFileName);
+ TEntry entry;
+ return(iFs.Entry(aFileName,entry)==KErrNone);
+
+ }
+
+TBool TNearestLanguageFileFinder::FindDrive()
+ {
+ ASSERT(iFileName);
+ TBool found=EFalse;
+ TInt driveLength=iDrives.Length();
+ for (TInt drive = 0; drive!=driveLength; ++drive)
+ {
+ (*iFileName)[0] = iDrives[drive];
+ if (FileExists(*iFileName))
+ {
+ found=ETrue;
+ break;
+ }
+ }
+ return found;
+ }
+
+TBool TNearestLanguageFileFinder::AppendLanguageCode(TLanguage aLanguage)
+ {
+ TInt rest = static_cast<TInt>(aLanguage);
+#ifdef _DEBUG
+ _LIT(KErrorMessage, "Bafl");
+#endif
+ __ASSERT_DEBUG(0 <= rest, User::Panic(KErrorMessage,KErrArgument));
+ iFileName->SetLength(iBaseLength);
+ const TInt remaining = iFileName->MaxLength() - iBaseLength;
+ TInt soFar = 0;
+ TBuf<1> num;
+ num.Append('0');
+ TBool appendLangSuccess = ETrue;
+ TInt digitCount = 0;
+ TInt digit = 0;
+ while (rest)
+ {
+ if (remaining == soFar)
+ {
+ // no more room in descriptor- return rather than panic,
+ // file cannot exist.
+ iFileName->SetLength(iBaseLength);
+ appendLangSuccess= EFalse;
+ break;
+ }
+ // Convert the number to ASCII by consistantly getting the base 10 remainder to convert.
+ // The number is updated minus the remainder for the next iteration.
+ // eg (rest = 123) -> (12, r3) -> (1, r2) -> (0, r1)
+ // Then insert the ASCII representation of the remainder into the filename end
+ // so it appears the correct way round.
+ // eg (filename.r) -> (filename.r3) -> (filename.r23) -> (filename.r123)
+ digit = rest % 10;
+ digitCount++;
+ rest /= 10;
+ num[0] = static_cast<TText16>(digit + '0');
+ iFileName->Insert(iBaseLength, num);
+
+ // Minimum suffix length is KInvNameAndMinSuffixLength
+ // so we have to insert zeros to make this up.
+ while (!rest && digitCount < KInvNameAndMinSuffixLength)
+ {
+ num[0] = static_cast<TText16>('0');
+ iFileName->Insert(iBaseLength, num);
+ ++digitCount;
+ }
+
+ ++soFar;
+ }
+
+ return appendLangSuccess;
+ }
+
+
+TBool TNearestLanguageFileFinder::FindLanguageAndDrive()
+/** Search for files across all drives in all languages in the path plus the
+language-neutral file. */
+ {
+ ASSERT(iFileName);
+ // No point appending if the suffix is bad
+ for (const TLanguage* currentLang = iPath; *currentLang != ELangNone; ++currentLang)
+ {
+ if (AppendLanguageCode(*currentLang) && FindDrive())
+ {
+ iLanguage = *currentLang;
+ return ETrue;
+ }
+ }
+ // search for language-neutral file
+ iFileName->SetLength(iBaseLength);
+ iFileName->Append(iSuffix);
+ return FindDrive();
+ }
+
+TInt TNearestLanguageFileFinder::LanguageNumberFromFile(const TDesC& aFileName, const TDesC& aStem)
+ {
+ TInt lang = 0;
+ TInt multiplier = 1;
+ TInt leadingZeroCount = 0;
+ TInt languageNumber = KErrNotFound;
+ const TText* firstChar = aFileName.Ptr();
+ const TText* lastChar = firstChar + aFileName.Length() - 1;
+ const TText* currentChar = lastChar;
+ // string cannot contain only numbers, because it must have a ':' in it
+ while ('0' <= *currentChar && *currentChar <= '9')
+ {
+ if (*currentChar == '0')
+ leadingZeroCount++;
+ else
+ {
+ leadingZeroCount = 0;
+ lang += multiplier * (*currentChar - '0');
+ }
+ multiplier *= 10;
+ --currentChar;
+ }
+ TInt along=lastChar - currentChar;
+ if (2 <= along)
+ {
+ // We have at least 2 digits at the end.
+ // trim of bad leading zeros
+ TInt maxTrim = along - 2;
+ if (maxTrim < leadingZeroCount)
+ {
+ leadingZeroCount = maxTrim;
+ }
+ currentChar += leadingZeroCount;
+ // we have at least 2 digits at the end but does the rest of it match the stem?
+ TPtrC foundStem(firstChar, currentChar - firstChar + 1);
+ //foundStem.CompareF(aStem.Right(foundStem.Length()))
+ if (0 == foundStem.CompareF(aStem))
+ {
+ languageNumber=lang;
+ }
+ }
+ return languageNumber;
+ }
+
+TInt TNearestLanguageFileFinder::FindFirstLanguageFile(RFs& aFs)
+ {
+ ASSERT(iFileName);
+ iFileName->SetLength(iBaseLength);
+ TPtrC name(*iFileName);
+ TParsePtrC nameToParse(name);
+ TPtrC nameStem(nameToParse.NameAndExt());
+ iFileName->Append('*');
+ TInt bestLanguageMatch = KMaxTInt;
+ RDirectoryScanner& scanner = DirectoryScanner();
+ TInt err = scanner.Open(aFs, *iFileName);
+ if (err != KErrNone)
+ {
+ return err;
+ }
+ TEntry entry;
+ while (KErrNone == scanner.Next(entry))
+ {
+ TInt lang = LanguageNumberFromFile(entry.iName, nameStem);
+ if (0 < lang && lang < bestLanguageMatch)
+ {
+ bestLanguageMatch = lang;
+ }
+ }
+ scanner.Close();
+ if (bestLanguageMatch != KMaxTInt)
+ {
+ iLanguage = static_cast<TLanguage>(bestLanguageMatch);
+ AppendLanguageCode(static_cast<TLanguage>(bestLanguageMatch));
+ return KErrNone;
+ }
+ return KErrNotFound;
+ }
+
+// Try each drive for any language files
+// iFileName must have a directory specifier
+TInt TNearestLanguageFileFinder::FindFirstLanguageFileAndDrive()
+ {
+ ASSERT(iFileName);
+ TInt findFirstResult=KErrNotFound;
+ TInt driveLength=iDrives.Length();
+ for (TInt drive = 0; drive != driveLength; ++drive)
+ {
+ (*iFileName)[0] = iDrives[drive];
+ TInt err = FindFirstLanguageFile(CONST_CAST(RFs&,iFs));
+ if (err == KErrNone || err == KErrNoMemory)
+ {
+ findFirstResult=err;
+ break;
+ }
+ }
+ return findFirstResult;
+ }
+
+/**
+Invalid filenames are any filename whose length (minus path) must be greater
+than KInvNameAndMinSuffixLength, and whose form is purely numerical, i.e. '1234'
+*/
+TBool TNearestLanguageFileFinder::SetFileName(TFileName& aFileName)
+ {
+ iDrives.Zero();
+ iFileName = &aFileName;
+ iOriginalBaseLength = iFileName->Length();
+
+ TInt suffixLength = CountDigitsFromEndInSuffix (aFileName);
+
+ // No point trying for filenames thats are badly formed
+ // or that are too large.
+ if (suffixLength >= 0 &&
+ KInvNameAndMinSuffixLength < iOriginalBaseLength)
+ {
+ if (suffixLength > 0)
+ {
+ // all of suffix to be replaced
+ iSuffix = iFileName->Right(suffixLength);
+ iOriginalBaseLength -= suffixLength;
+ iFileName->SetLength(iOriginalBaseLength);
+ }
+ else
+ {
+ // No numerical part to suffix
+ TInt periodIdx = 0;
+
+ // Search for the period within range KInvNameAndMinSuffixLength
+ // from the end. As this must work for all values of
+ // KInvNameAndMinSuffixLength
+ for (TInt i = iOriginalBaseLength-1;
+ !periodIdx && i >= (iOriginalBaseLength-KInvNameAndMinSuffixLength-1);
+ --i)
+ {
+ if ((*iFileName) [i] == '.')
+ {
+ periodIdx = i;
+ }
+ }
+
+ // Don't handle files ending in a period.
+ // This is because the behaviour is different between Windows
+ // and Symbian Fs. In Windows it strips the period off.
+ //
+ // However, and this shouldn't happen as it is not shown
+ // (in the documentation) to be valid.
+ // Just try our best.
+ if (periodIdx == iOriginalBaseLength-1)
+ {
+ iSuffix.Zero();
+ return EFalse;
+ }
+ else
+ if (periodIdx)
+ {
+ // If there are KInvNameAndMinSuffixLength chars after the period
+ // simply replace them.
+ TInt right = iOriginalBaseLength-periodIdx-1;
+ iSuffix = iFileName->Right(right);
+ iOriginalBaseLength -= right;
+ iFileName->SetLength(iOriginalBaseLength);
+ }
+ else
+ {
+ // Make the suffix start from KInvNameAndMinSuffixLength
+ // from the right
+ TInt right = KInvNameAndMinSuffixLength;
+ iSuffix = iFileName->Right(right);
+ iOriginalBaseLength -= right;
+ iFileName->SetLength(iOriginalBaseLength);
+ }
+ }
+ }
+ else
+ {
+ // bad or no suffix - treat the same
+ iSuffix.Zero();
+ return EFalse;
+ }
+
+ // For filenames with no drive letter prefix and also for filenames
+ // shorter than the drive letter length, i.e. with no drive
+ // information, insert it.
+ // Handles if the user simply enters the drive, e.g. "c:".
+ if (iOriginalBaseLength < KMaxDriveName || (*iFileName)[1] != ':')
+ {
+ // Set up the default if none supplied and make room in the filename
+ // array to contain a drive specification. Set initial drive letter to -1
+ // so the iFileName is repaired before exited
+ iInitialDriveLetter = -1;
+ iFileName->Insert(0, _L("_:"));
+ iDrives.Append('Z');
+ }
+ else
+ {
+ // Use the drive supplied inthe aName to NearestLanguageFile()
+ iInitialDriveLetter = (*iFileName)[0];
+ iDrives.Append(iInitialDriveLetter);
+ }
+
+ iBaseLength = iFileName->Length();
+
+ return ETrue;
+ }
+
+
+TLanguage TNearestLanguageFileFinder::Language()
+ {
+ return iLanguage;
+ }
+
+TNearestLanguageFileFinder::TNearestLanguageFileFinder(
+ const RFs& aFs)
+ : iFs(aFs), iFileName(0), iLanguage(ELangNone)
+ {
+ }
+
+void TNearestLanguageFileFinder::RepairFileName()
+ {
+ ASSERT(iFileName);
+ iFileName->SetLength(iBaseLength);
+ if (iInitialDriveLetter == -1)
+ iFileName->Delete(0, 2);
+ else
+ (*iFileName)[0] = static_cast<TText>(iInitialDriveLetter);
+ iFileName->SetLength(iOriginalBaseLength);
+ iFileName->Append(iSuffix);
+ }
+
+
+/**
+Add the custom resource drive to the start of the iDrives string.
+
+The custom resource drive is a preset writeable drive on which customised
+resource files may be present. This drive takes priority over the other
+drives when searching for language files.
+
+@return KErrNone if iDrives string was successfully modified; KErrAlreadyExists
+if the drive is already present in the string; otherwise one of
+the other system-wide error codes (iDrives will be unmodified).
+*/
+TInt TNearestLanguageFileFinder::AddCustomResourceDrive()
+ {
+ TInt drive = GetCustomResourceDriveNumber();
+ if (drive<0)
+ return drive;
+
+ // if drive not already in drive list
+ if (iDrives.LocateF('A' + drive) < 0)
+ {
+ // add it
+ _LIT(KDrivePlaceholder, "_");
+ iDrives.Insert(0, KDrivePlaceholder);
+ iDrives[0] = 'A' + drive;
+ return KErrNone;
+ }
+ else
+ return KErrAlreadyExists;
+ }
+
+
+void TNearestLanguageFileFinder::AddAllDrives()
+ {
+ ASSERT(iDrives.Length() < 2);
+ if (iDrives.Length() == 0)
+ {
+ iDrives = KAllDrives;
+ return;
+ }
+ TInt pos = KAllDrives().LocateF(iDrives[0]);
+ if (pos < 0)
+ {
+ iDrives = KAllDrives;
+ return;
+ }
+ iDrives.Append(KAllDrives().Left(pos));
+ iDrives.Append(KAllDrives().Mid(pos + 1));
+ }
+
+
+/**
+Get the value of the custom resource drive.
+
+The custom resource drive is a preset writeable drive on which customised language resource
+files can reside. The drive number is accessed via the HAL attribute ECustomResourceDrive.
+It is then returned if it has been defined as a valid drive no.
+Otherwise for backward compatibility reasons an attempt is then made to access the system
+drive HAL attribute instead. This drive number is returned if it has been defined as a valid
+drive number.
+Otherwise if neither a valid ECustomResourceDrive or ESystemDrive exists then KErrNotFound
+is returned.
+
+Note that the ESystemDrive HAL attribute has been deprecated. It is accessed here to cater
+for existing implementations which still expect it to be used.
+
+@return The drive number (corresponding to a TDriveNumber value) if successful;
+KErrNotFound if neither a valid ECustomResourceDrive or a valid ESystemDrive HAL attribute
+is defined;
+
+@see HAL::ECustomResourceDrive
+@see HAL::ESystemDrive
+*/
+TInt TNearestLanguageFileFinder::GetCustomResourceDriveNumber() const
+ {
+ TInt drive = KErrNotFound;
+
+ // access custom resource drive attribute
+ if (HAL::Get(HAL::ECustomResourceDrive, drive) == KErrNone)
+ {
+ // check that drive is valid
+ if (drive>=EDriveA && drive<=EDriveZ)
+ return drive;
+ }
+
+ // access system drive attribute
+ // (Note that ESystemDrive is deprecated. It is checked here
+ // solely for backward compatibility reasons.)
+ if (HAL::Get(HAL::ESystemDrive, drive) == KErrNone)
+ {
+ // check that drive is valid
+ if (drive>=EDriveA && drive<=EDriveZ)
+ return drive;
+ }
+
+ return KErrNotFound;
+ }
+
+