charconvfw/numbergrouping/Src/NumberGrouping.cpp
branchRCL_3
changeset 55 336bee5c2d35
equal deleted inserted replaced
54:748ec5531811 55:336bee5c2d35
       
     1 /*
       
     2 * Copyright (c) 2002-2008 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: 
       
    15 *
       
    16 */
       
    17 
       
    18 #include "NumberGrouping.h"
       
    19 #include "RegularExpression.h"
       
    20 #include "cleanuputil.h"
       
    21 
       
    22 #include <barsread.h>
       
    23 #include <eikenv.h>
       
    24 #include <centralrepository.h>
       
    25 #include <NumberGroupingCRKeys.h>
       
    26 
       
    27 const TText KNumberGroupingWildcard('n');
       
    28 const TText KNumberGroupingOneOrMoreCharactersToken('~');
       
    29 
       
    30 // This constant represents all the valid whitespace that we can handle. Several
       
    31 // APIs give whitespace special significance as a formatting character, especially
       
    32 // those methods used to obtain text for formatting into fixed-width UI elements
       
    33 // where spaces are not to be rendered at the margins.
       
    34 const TText KNumberGroupingSpace(' ');
       
    35 
       
    36 const TInt KMinimumLengthToGroup = 1; // No grouping occurs if fewer than this in unformatted buffer
       
    37 
       
    38 #include <numbergrouping.rsg>
       
    39 
       
    40 
       
    41 GLDEF_C void Panic(TNumberGroupingPanic aPanic)
       
    42     {
       
    43     _LIT(KPanicCat,"Number Grouping");
       
    44     User::Panic(KPanicCat, aPanic);
       
    45     }
       
    46 
       
    47 // Valid phone number characters apart from IsDigit() characters are listed here
       
    48 const TText KAdditionalPhoneNumberCharacters[] = {'+','#','*','p','w'};
       
    49 
       
    50 
       
    51 
       
    52 
       
    53 NONSHARABLE_CLASS(CPNGNumberGroupingExtension): public CBase
       
    54 	{
       
    55 public:
       
    56 	CPNGNumberGroupingExtension();
       
    57 	~CPNGNumberGroupingExtension();
       
    58 public:
       
    59     TInt        iMaxExtraCharacters; 
       
    60     TInt        iNumberGroupingCRValue;
       
    61     };
       
    62 
       
    63 CPNGNumberGroupingExtension::CPNGNumberGroupingExtension()
       
    64 	{
       
    65 	}
       
    66 
       
    67 CPNGNumberGroupingExtension::~CPNGNumberGroupingExtension()
       
    68 	{
       
    69 	}
       
    70 
       
    71 
       
    72 
       
    73 CPNGNumberGrouping::TPNGSeparator::TPNGSeparator()
       
    74     : iPosition(-1), iSeparatorCharacter(KNumberGroupingSpace)
       
    75     {
       
    76     }
       
    77 
       
    78 CPNGNumberGrouping::TPNGSeparator::TPNGSeparator( TInt aPosition, TText aSeparatorCharacter )
       
    79     : iPosition(aPosition), iSeparatorCharacter(aSeparatorCharacter)
       
    80     {
       
    81     }
       
    82 
       
    83 CPNGNumberGrouping::TPNGGroupingInfo::TPNGGroupingInfo()
       
    84     {
       
    85     }
       
    86 
       
    87 // CPNGNumberGrouping - grouping engine class
       
    88 CPNGNumberGrouping::CPNGNumberGrouping(TInt aMaxLength /* = 0 */, TBool aReversed /* = EFalse */) :
       
    89                                             iForceLanguage(ELangTest),
       
    90                                             iUnformattedNumberPtr(KNullDesC),
       
    91                                             iFormattedNumberPtr(KNullDesC),
       
    92                                             iReverseFormattedNumberPtr(KNullDesC),
       
    93                                             iSelectionPtr(KNullDesC),
       
    94                                             iLanguage(ELangTest),
       
    95                                             iMaxUnformattedLength(aMaxLength),
       
    96                                             iReversed(aReversed),
       
    97                                             iMatchedPatternIndex(ENoMatchedPattern)
       
    98     {
       
    99     }
       
   100 
       
   101 EXPORT_C CPNGNumberGrouping* CPNGNumberGrouping::NewL( TInt aMaxLength, TBool aReserved)
       
   102     {
       
   103     CPNGNumberGrouping* s = NewLC(aMaxLength, aReserved);
       
   104     CleanupStack::Pop();
       
   105     return s;
       
   106     }
       
   107 
       
   108 EXPORT_C CPNGNumberGrouping* CPNGNumberGrouping::NewLC( TInt aMaxLength, TBool aReserved)
       
   109     {
       
   110     CPNGNumberGrouping* s = new(ELeave)CPNGNumberGrouping( aMaxLength, aReserved);
       
   111     CleanupStack::PushL(s);
       
   112     s->ConstructL();
       
   113     return s;
       
   114     }
       
   115 
       
   116 void CPNGNumberGrouping::ConstructL()
       
   117     {
       
   118     iExtension = new (ELeave) CPNGNumberGroupingExtension(); 
       
   119     CRepository* repository = NULL;
       
   120     iExtension->iNumberGroupingCRValue = 0;
       
   121     TRAPD(ret, repository = CRepository::NewL(KCRUidNumberGrouping));
       
   122     if (ret == KErrNone)
       
   123         {
       
   124         ret = repository->Get(KNumberGrouping, iExtension->iNumberGroupingCRValue);
       
   125         }
       
   126     delete repository;
       
   127     
       
   128     // Read from resource first in order to obtain a value for iExtension->iMaxExtraCharacters
       
   129     iLanguage = doReadLanguageFromSharedData();
       
   130     doReadFormatInfoFromResourceFileL(); // This sets iExtension->iMaxExtraCharacters
       
   131 
       
   132     // Allocation of buffers.  Note that iMaxUnformattedLength must be retained as member
       
   133     // data because HBufCs may come back with more storage available than asked for.
       
   134     // The uncertainty in the actual length provided by the HBufCs means that although
       
   135     // they are asked to be different by iExtension->iMaxExtraCharacters, the difference between the
       
   136     // actual MaxLengths may be less than iMaxExtraCharcaters.
       
   137     // The approach decided upon is to retain iMaxUnformattedLength as member data and
       
   138     // use IT everywhere throughout this class's implementation, NEVER using
       
   139     // iUnformattedNumber->Des().MaxLength()
       
   140     iUnformattedNumber = HBufC::NewL(iMaxUnformattedLength);
       
   141     iFormattedNumber = HBufC::NewL(iMaxUnformattedLength + iExtension->iMaxExtraCharacters);
       
   142 
       
   143     // Create revesed buffer only if requested
       
   144     if ( iReversed )
       
   145         iReverseFormattedNumber = HBufC::NewL(iMaxUnformattedLength + iExtension->iMaxExtraCharacters);        
       
   146     }
       
   147 
       
   148 EXPORT_C CPNGNumberGrouping::~CPNGNumberGrouping()
       
   149     {
       
   150     doClearGroupingItemsList();
       
   151 
       
   152     delete iUnformattedNumber;
       
   153     delete iFormattedNumber;
       
   154     delete iReverseFormattedNumber;
       
   155     delete iRegExp;
       
   156     delete iExtension;
       
   157     }
       
   158 
       
   159 EXPORT_C TInt CPNGNumberGrouping::Insert(TInt aIndex, TText aChar)
       
   160     {
       
   161 
       
   162     if( aIndex >= 0 && aIndex <= iUnformattedNumber->Length())
       
   163         {
       
   164 
       
   165         if(iUnformattedNumber->Length() >= iMaxUnformattedLength)
       
   166             return KErrOverflow;
       
   167 
       
   168         TBuf<1> bufChar(1);
       
   169         bufChar[0] = aChar;
       
   170         TPtr ptrModifyable(iUnformattedNumber->Des());
       
   171         ptrModifyable.Insert(aIndex, bufChar);
       
   172 
       
   173         doClearFormattedNumbers();
       
   174 
       
   175         return KErrNone;
       
   176         }
       
   177 
       
   178     return KErrIndexOutOfRange;
       
   179     }
       
   180 
       
   181 EXPORT_C TInt CPNGNumberGrouping::Delete(TInt aIndex)
       
   182     {
       
   183     if(aIndex >= 0 && aIndex < iUnformattedNumber->Length())
       
   184         {
       
   185         TPtr ptrModifyable(iUnformattedNumber->Des());
       
   186         ptrModifyable.Delete(aIndex, KSingleCharacter);
       
   187 
       
   188         doClearFormattedNumbers();
       
   189 
       
   190         return KErrNone;
       
   191         }
       
   192 
       
   193     return KErrIndexOutOfRange;
       
   194     }
       
   195 
       
   196 EXPORT_C TInt CPNGNumberGrouping::Append(TText aChar)
       
   197     {
       
   198     if(iUnformattedNumber->Length() >= iMaxUnformattedLength)
       
   199         return KErrOverflow;
       
   200 
       
   201     TBuf<1> bufChar(1);
       
   202     bufChar[0] = aChar;
       
   203     TPtr ptrModifyable(iUnformattedNumber->Des());
       
   204     ptrModifyable.Append(bufChar);
       
   205 
       
   206     doClearFormattedNumbers();
       
   207 
       
   208     return KErrNone;
       
   209     }
       
   210 
       
   211 EXPORT_C TInt CPNGNumberGrouping::Set(const TDesC& aNumber)
       
   212     {
       
   213     if( aNumber.Length() > iMaxUnformattedLength )
       
   214         return KErrOverflow;
       
   215 
       
   216     TPtr ptrModifyable( iUnformattedNumber->Des() );
       
   217     ptrModifyable.Copy( aNumber );
       
   218 
       
   219     doClearFormattedNumbers();
       
   220 
       
   221     return KErrNone;
       
   222     }
       
   223 
       
   224 EXPORT_C TInt CPNGNumberGrouping::Length() const
       
   225     {
       
   226     if(!iFormattedNumber->Length()) // This test is used as the trigger to reformat
       
   227         FormattedNumber();
       
   228 
       
   229     return iFormattedNumber->Length();
       
   230     }
       
   231 
       
   232 EXPORT_C TInt CPNGNumberGrouping::UnFormattedLength() const
       
   233     {
       
   234     return iUnformattedNumber->Length();
       
   235     }
       
   236 
       
   237 EXPORT_C TInt CPNGNumberGrouping::MaxDisplayLength() const
       
   238     {
       
   239     // Despite its name, this method returns the max length of the UNFORMATTED buffer
       
   240     // This must not be implemented to return the actual length available in
       
   241     // iUnformattedBuffer.
       
   242     return iMaxUnformattedLength;
       
   243     }
       
   244 
       
   245 EXPORT_C TBool CPNGNumberGrouping::IsSpace(TInt aPos) const
       
   246     {
       
   247     // Very tricky semantics for this.  Must be a space inserted by the formatting
       
   248     if ( iFormattedNumber->Length() > aPos &&
       
   249          iFormattedNumber->operator[](aPos) == KNumberGroupingSpace &&
       
   250          // Check also that is is less than the length to group + inserted characters
       
   251          aPos < ( LengthToGroup() + (iFormattedNumber->Length() - iUnformattedNumber->Length()) ) )
       
   252         return ETrue;
       
   253     else
       
   254         return EFalse;
       
   255     }
       
   256 
       
   257 EXPORT_C const TDesC& CPNGNumberGrouping::FormattedNumber(TInt aFrom, TInt aTo) const
       
   258     {
       
   259     if(iUnformattedNumber->Length() != 0 &&
       
   260         aFrom >= 0 &&
       
   261         aFrom <= aTo )
       
   262         {
       
   263         FormattedNumber();
       
   264         iFormattedNumberPtr.Set(KNullDesC);
       
   265 
       
   266         TInt length = iFormattedNumber->Length();
       
   267         if(aTo < length + 1)
       
   268             {
       
   269             TInt length = iFormattedNumber->Length();
       
   270             if ( iExtension->iNumberGroupingCRValue )
       
   271             	{
       
   272 				// Advance to the next non-space
       
   273 				while( (aFrom < length ) && (*iFormattedNumber)[aFrom] == KNumberGroupingSpace )
       
   274 					{
       
   275 					++aFrom;
       
   276 					}
       
   277 	
       
   278 				// Retreat to the last non-space
       
   279 				while( (aTo > 0) && (*iFormattedNumber)[aTo] == KNumberGroupingSpace )
       
   280 					{
       
   281 					--aTo;
       
   282 					}
       
   283             	}
       
   284 
       
   285             // Does fetching the descriptor still make sense?
       
   286             if ( (0 <= aFrom) && (aFrom <= aTo) && (aTo < length) )
       
   287                 iFormattedNumberPtr.Set( iFormattedNumber->Mid( aFrom, aTo-aFrom+1 ) );
       
   288             }
       
   289         }
       
   290     else
       
   291         {
       
   292         if(iFormattedNumber->Length())
       
   293             {
       
   294             CPNGNumberGrouping* pThis = const_cast<CPNGNumberGrouping*>(this);
       
   295             pThis->doClearFormattedNumbers();
       
   296             }
       
   297 
       
   298         iFormattedNumberPtr.Set(KNullDesC);
       
   299         }
       
   300 
       
   301     return iFormattedNumberPtr;
       
   302     }
       
   303 
       
   304 EXPORT_C const TDesC& CPNGNumberGrouping::FormattedNumber() const
       
   305     {
       
   306     if( !iFormattedNumber->Length() )
       
   307         {
       
   308         TInt err = KErrNone;
       
   309 
       
   310         CPNGNumberGrouping* pThis = const_cast<CPNGNumberGrouping*>(this);
       
   311 
       
   312         if( LengthToGroup() < KMinimumLengthToGroup || !iExtension->iNumberGroupingCRValue )
       
   313             {
       
   314             // This is now just a short cut, as doNumberGroupingL handles premature truncation of
       
   315             // formatting. But this avoids all the language checking
       
   316             doNumberSquashing();  // copies the unformatted number straight into the formatted number
       
   317             }
       
   318         else
       
   319             {
       
   320             TLanguage eLanguage;
       
   321             if(iForceLanguage != ELangTest)
       
   322                 eLanguage = iForceLanguage;
       
   323             else
       
   324                 eLanguage = doReadLanguageFromSharedData();
       
   325 
       
   326             if(eLanguage != iLanguage)
       
   327                 {
       
   328                 iLanguage = eLanguage;
       
   329 
       
   330                 TRAP(err, pThis->doReadFormatInfoFromResourceFileL());
       
   331                 if(err != KErrNone)
       
   332                     {
       
   333                     iFormattedNumberPtr.Set(KNullDesC);
       
   334                     return iFormattedNumberPtr;
       
   335                     }
       
   336                 }
       
   337 
       
   338             TRAP(err, doNumberGroupingL());
       
   339             }
       
   340 
       
   341         if(err != KErrNone)
       
   342             pThis->doClearFormattedNumbers();
       
   343         else
       
   344             iFormattedNumberPtr.Set(iFormattedNumber->Ptr(), iFormattedNumber->Length());
       
   345         }
       
   346 
       
   347     return iFormattedNumberPtr;
       
   348     }
       
   349 
       
   350 EXPORT_C const TDesC& CPNGNumberGrouping::ReverseFormattedNumber(TInt aFrom, TInt aTo) const
       
   351     {
       
   352     if ( iReversed )
       
   353         {
       
   354         if(iUnformattedNumber->Length() != 0 &&
       
   355             aFrom >= 0 &&
       
   356             aFrom <= aTo)
       
   357             {
       
   358             ReverseFormattedNumber();
       
   359 
       
   360             iReverseFormattedNumberPtr.Set(KNullDesC);
       
   361 
       
   362             TInt length = iReverseFormattedNumber->Length();
       
   363             if( aTo < length + 1 )
       
   364                 {
       
   365                 // Advance to the next non-space
       
   366                 if( iExtension->iNumberGroupingCRValue )
       
   367                 	{
       
   368 					while( (aFrom < length ) && (*iReverseFormattedNumber)[aFrom] == KNumberGroupingSpace )
       
   369 						{
       
   370 						++aFrom;
       
   371 						}
       
   372 	
       
   373 					// Retreat to the last non-space
       
   374 					while( (aTo > 0) && (*iReverseFormattedNumber)[aTo] == KNumberGroupingSpace )
       
   375 						{
       
   376 						--aTo;
       
   377 						}
       
   378                 	}
       
   379 
       
   380                 // Does fetching the descriptor still make sense?
       
   381                 if ( (0 <= aFrom) && (aFrom <= aTo) && (aTo < length) )
       
   382                     iReverseFormattedNumberPtr.Set(
       
   383                         iReverseFormattedNumber->Mid( aFrom, aTo-aFrom+1) );
       
   384                 }
       
   385             }
       
   386         else
       
   387             iReverseFormattedNumberPtr.Set(KNullDesC);
       
   388         }
       
   389 
       
   390     return iReverseFormattedNumberPtr; // Zero initialized at construction
       
   391     }
       
   392 
       
   393 EXPORT_C const TDesC& CPNGNumberGrouping::ReverseFormattedNumber() const
       
   394     {
       
   395     if( iReverseFormattedNumber && !iReverseFormattedNumber->Length())
       
   396         {
       
   397         if(!iFormattedNumber->Length())
       
   398             FormattedNumber();
       
   399 
       
   400         TInt nLength = iFormattedNumber->Length();
       
   401 
       
   402         TPtr ptrModifyable(iReverseFormattedNumber->Des());
       
   403         TBuf<1> bufChar(1);
       
   404 
       
   405         for(TInt i = nLength; i > 0; --i)
       
   406             {
       
   407             TText cChar = (*iFormattedNumber)[i-1];
       
   408             bufChar[0] = cChar;
       
   409             ptrModifyable.Insert(nLength - i, bufChar);
       
   410             }
       
   411 
       
   412         iReverseFormattedNumberPtr.Set(iReverseFormattedNumber->Ptr(), nLength);
       
   413         }
       
   414 
       
   415     return iReverseFormattedNumberPtr;
       
   416     }
       
   417 
       
   418 EXPORT_C const TDesC& CPNGNumberGrouping::Selection(TInt aFrom, TInt aTo) const
       
   419     {
       
   420     if(aFrom < iUnformattedNumber->Length())
       
   421         {
       
   422         TPtr ptrUnformatted = iUnformattedNumber->Des();
       
   423         iSelectionPtr.Set(&(ptrUnformatted[aFrom]), aTo - aFrom);
       
   424         }
       
   425     else
       
   426         iSelectionPtr.Set(KNullDesC);
       
   427 
       
   428     return iSelectionPtr;
       
   429     }
       
   430 
       
   431 EXPORT_C const TDesC&   CPNGNumberGrouping::UnFormattedNumber(TInt aFrom, TInt aTo) const
       
   432     {
       
   433     if (iUnformattedNumber && aFrom >= 0 && aFrom <= aTo && aTo < iUnformattedNumber->Length())
       
   434         {
       
   435         iUnformattedNumberPtr.Set(&((*iUnformattedNumber)[aFrom]), aTo - aFrom + 1);
       
   436         }
       
   437     else
       
   438         {
       
   439         iUnformattedNumberPtr.Set(KNullDesC);
       
   440         }
       
   441     return iUnformattedNumberPtr;
       
   442     }
       
   443 
       
   444 EXPORT_C const TDesC& CPNGNumberGrouping::UnFormattedNumber() const
       
   445     {
       
   446     return UnFormattedNumber(0, iUnformattedNumber->Length() - 1);
       
   447     }
       
   448 
       
   449 TLanguage CPNGNumberGrouping::doReadLanguageFromSharedData() const
       
   450     {    
       
   451     if (iExtension->iNumberGroupingCRValue)
       
   452         {
       
   453         return ELangAmerican;
       
   454         }
       
   455     else
       
   456         {
       
   457         return ELangTest;
       
   458         }
       
   459     }
       
   460 
       
   461 void CPNGNumberGrouping::doClearFormattedNumbers()
       
   462     {
       
   463     TPtr ptrModifyable( iUnformattedNumber->Des() );
       
   464 
       
   465     for (TInt index = 0; index < ptrModifyable.Length(); index++)
       
   466         {
       
   467         TChar ch = TChar(ptrModifyable[index]);
       
   468         ch.Fold( TChar::EFoldDigits | TChar::EFoldSpaces);
       
   469         }
       
   470     
       
   471     iFormattedNumber->Des().Zero();
       
   472     iFormattedNumberPtr.Set(KNullDesC);
       
   473 
       
   474     if ( iReverseFormattedNumber )
       
   475         iReverseFormattedNumber->Des().Zero();
       
   476 
       
   477     iReverseFormattedNumberPtr.Set(KNullDesC);
       
   478     iMatchedPatternIndex = ENoMatchedPattern;
       
   479     }
       
   480 
       
   481 void CPNGNumberGrouping::doReadFormatInfoFromResourceFileL()
       
   482     {
       
   483     doClearGroupingItemsList();
       
   484     delete iRegExp;
       
   485     iRegExp = NULL;
       
   486 
       
   487     RPointerArray<TDesC> parrGroupingPatternsList;
       
   488     CleanupResetAndDestroyPushL(parrGroupingPatternsList);
       
   489 
       
   490     TInt maxExtraCharacters(0);
       
   491 
       
   492     RFs fs;
       
   493     CleanupClosePushL(fs);
       
   494     if(fs.Connect() == KErrNone)
       
   495         {
       
   496         RResourceFile resourceFile;
       
   497         CleanupClosePushL(resourceFile);
       
   498 
       
   499         resourceFile.OpenL(fs, _L("z:\\resource\\numbergrouping.rsc"));
       
   500         HBufC8* bufResource = resourceFile.AllocReadL(R_GROUPING_MAPPING);
       
   501 
       
   502         TResourceReader resourceReader;
       
   503         resourceReader.SetBuffer(bufResource);
       
   504 
       
   505         TInt    nLanguageCount = resourceReader.ReadInt8();
       
   506         TBool   bLanguageMatches = EFalse;
       
   507 
       
   508         while(nLanguageCount-- || !bLanguageMatches)
       
   509             {
       
   510             TBool bLanguageMatches = (resourceReader.ReadInt8() == iLanguage);
       
   511 
       
   512             if(bLanguageMatches || ((nLanguageCount == -1) && !bLanguageMatches))
       
   513                 {
       
   514                 TInt nGroupingSchemeCount = resourceReader.ReadInt8();
       
   515 
       
   516                 while(nGroupingSchemeCount--)
       
   517                     {
       
   518                     TInt thisMaxExtraCharacters(0);
       
   519                     ReadGroupingSchemeL(
       
   520                         resourceReader, parrGroupingPatternsList, thisMaxExtraCharacters );
       
   521                     // take this new max extra characters if bigger
       
   522                     maxExtraCharacters = Max( maxExtraCharacters, thisMaxExtraCharacters );
       
   523                     }
       
   524 
       
   525                 break; // This breaks out because we take the first language that matches
       
   526 
       
   527                 } // End of if on language/locale test
       
   528             else  // skip other locales
       
   529                 {
       
   530                 TInt nGroupingSchemeCount = resourceReader.ReadInt8();
       
   531                 while(nGroupingSchemeCount--)
       
   532                     {
       
   533                     SkipGroupingSchemeL( resourceReader );
       
   534                     }
       
   535                 }
       
   536             }
       
   537 
       
   538         delete bufResource;
       
   539 
       
   540         resourceFile.Close();
       
   541         CleanupStack::Pop();  // resource file
       
   542         }
       
   543 
       
   544     fs.Close();
       
   545     CleanupStack::Pop();  // file system
       
   546 
       
   547     iExtension->iMaxExtraCharacters = maxExtraCharacters; // Latch the high water mark of extra characters
       
   548 
       
   549     iRegExp = CRegularExpression::NewL(&parrGroupingPatternsList);
       
   550 
       
   551     CleanupStack::PopAndDestroy(&parrGroupingPatternsList);  // patterns list
       
   552     }
       
   553 
       
   554 void CPNGNumberGrouping::doNumberGroupingL() const
       
   555     {
       
   556     TInt lengthToGroup = LengthToGroup();
       
   557 
       
   558     if ( lengthToGroup >= KMinimumLengthToGroup )
       
   559         {
       
   560 
       
   561         TInt matchedPattern = KErrNotFound;
       
   562         TInt newMatchedPattern = KErrNotFound;
       
   563 
       
   564         // Search for matches in the RegExp object. It returns the next matching pattern
       
   565         // However, even if there is a match, lengthToGroup may not be in the deployment
       
   566         // length range between minDigits and MaxDigits, inclusive
       
   567         do  {
       
   568             // Check for another matching pattern
       
   569             newMatchedPattern = iRegExp->SearchFrom( newMatchedPattern+1, *iUnformattedNumber);
       
   570 
       
   571             if( newMatchedPattern != KErrNotFound) // Found a match, but it is OK?
       
   572                 {
       
   573 
       
   574                 TInt minDigits = iGroupingItemsList[newMatchedPattern]->iMinNumberOfDigits;
       
   575                 TInt maxDigits = iGroupingItemsList[newMatchedPattern]->iMaxNumberOfDigits;
       
   576 
       
   577                 // Fill in sensible values for min and max if not present
       
   578                 if(minDigits == -1)
       
   579                     minDigits = 0;
       
   580                 if(maxDigits == -1)
       
   581                     maxDigits = lengthToGroup;
       
   582 
       
   583                 if ( minDigits <= lengthToGroup && lengthToGroup <= maxDigits )
       
   584                     {
       
   585                     matchedPattern = newMatchedPattern; // accept this new pattern
       
   586                     break;
       
   587                     }
       
   588                 }
       
   589 
       
   590             } while ( newMatchedPattern != KErrNotFound  );
       
   591 
       
   592         // Actually go and do the grouping
       
   593         if ( matchedPattern != KErrNotFound )
       
   594             {
       
   595             doNumberGroupingForPatternL( matchedPattern, lengthToGroup );
       
   596             return;
       
   597             }
       
   598 
       
   599         }
       
   600 
       
   601     // if we get to here, either the string was not matched to any of the patterns or the
       
   602     // unformatted string is exactly the display length.  In either case we call
       
   603     // doNumberSquashing() which simply leaves the string as it is...
       
   604     doNumberSquashing();
       
   605 
       
   606     }
       
   607 
       
   608 
       
   609 void CPNGNumberGrouping::doNumberGroupingForPatternL( TInt aMatchingPattern, TInt aLengthToGroup ) const
       
   610     {
       
   611     iMatchedPatternIndex = aMatchingPattern;
       
   612 
       
   613     TInt nLowPos = 0;
       
   614     TInt nHighPos = 0;
       
   615 
       
   616     TPtr desUnformattedNumber = iUnformattedNumber->Des();
       
   617     TInt unformattedLength = iUnformattedNumber->Length();
       
   618 
       
   619     __ASSERT_ALWAYS( aLengthToGroup <= unformattedLength , Panic(ENumberGroupingBadLengthToGroup) );
       
   620 
       
   621     TPNGGroupingInfo* matchedPattern = iGroupingItemsList[iMatchedPatternIndex];
       
   622     TInt nAfterCount = matchedPattern->iAfterPositions.Count();
       
   623     TBool bBeforePosition = (matchedPattern->iBeforePosition.iPosition == -1)?0:1;
       
   624 
       
   625     // Test to see if the beforePosition can be used with the current text length.
       
   626     // The following does not allow the before position to be used if it would result in an
       
   627     // insertion right next to one from the AfterPositions.
       
   628     // That is, tildas in the formatting string represent 1 or more characters.
       
   629     // e.g. if the last afterPosition is 4 and the before position is 3, then a 7 digit
       
   630     // number will not be able to have the before position used.
       
   631     if( nAfterCount &&
       
   632         (unformattedLength - matchedPattern->iBeforePosition.iPosition) <=
       
   633         matchedPattern->iAfterPositions[nAfterCount - 1].iPosition)
       
   634         {
       
   635         bBeforePosition = EFalse;
       
   636         }
       
   637 
       
   638     TPtr ptrModifyable(iFormattedNumber->Des());
       
   639 
       
   640     for(TInt i  = 0; i < nAfterCount && nHighPos < aLengthToGroup ; ++i)
       
   641         {
       
   642         nHighPos = matchedPattern->iAfterPositions[i].iPosition;
       
   643         if ( nHighPos >= aLengthToGroup )
       
   644             break;
       
   645 
       
   646         if(nHighPos < unformattedLength)
       
   647             {
       
   648             ptrModifyable.Append( desUnformattedNumber.Mid( nLowPos, nHighPos - nLowPos) );
       
   649             ptrModifyable.Append(matchedPattern->iAfterPositions[i].iSeparatorCharacter);
       
   650             nLowPos = nHighPos;
       
   651             }
       
   652         }
       
   653 
       
   654     // Do not do "before end" formatting at all if there is any truncation
       
   655     if ( aLengthToGroup < unformattedLength )
       
   656         {
       
   657         TInt nBeforePosition = matchedPattern->iBeforePosition.iPosition;
       
   658 
       
   659         if(bBeforePosition && nBeforePosition < unformattedLength)
       
   660             {
       
   661             nHighPos = unformattedLength - nBeforePosition;
       
   662             ptrModifyable.Append( desUnformattedNumber.Mid( nLowPos, nHighPos - nLowPos) );
       
   663             ptrModifyable.Append( matchedPattern->iBeforePosition.iSeparatorCharacter );
       
   664             nLowPos = nHighPos;
       
   665             }
       
   666         }
       
   667 
       
   668     nHighPos = unformattedLength;
       
   669     ptrModifyable.Append( desUnformattedNumber.Mid( nLowPos, nHighPos - nLowPos) );
       
   670 
       
   671     }
       
   672 
       
   673 void CPNGNumberGrouping::doNumberSquashing() const
       
   674     {
       
   675     __ASSERT_ALWAYS( !iFormattedNumber->Length(), Panic(ENumberGroupingFormattedNumberAlreadyExists) );
       
   676 
       
   677     // just copy from one t'other...
       
   678     TPtr ptrModifyable(iFormattedNumber->Des());
       
   679     ptrModifyable.Copy(*iUnformattedNumber);
       
   680     iMatchedPatternIndex = ENoMatchedPattern;
       
   681     }
       
   682 
       
   683 void CPNGNumberGrouping::doClearGroupingItemsList()
       
   684     {
       
   685     TInt nCount = iGroupingItemsList.Count();
       
   686 
       
   687     for(TInt i = 0; i < nCount; ++i)
       
   688         {
       
   689         iGroupingItemsList[i]->iAfterPositions.Close();
       
   690         delete iGroupingItemsList[i];
       
   691         iGroupingItemsList[i] = NULL;
       
   692         }
       
   693     iGroupingItemsList.Close();
       
   694     }
       
   695 
       
   696 void CPNGNumberGrouping::ReadGroupingSchemeL(
       
   697     TResourceReader& aResourceReader,
       
   698     RPointerArray<TDesC>& aGroupingPatternsList,
       
   699     TInt& aMaxExtraCharacters )
       
   700     {
       
   701     CleanupResetAndDestroyPushL(aGroupingPatternsList);
       
   702     TPNGGroupingInfo* groupingInfo = new (ELeave) TPNGGroupingInfo;
       
   703     CleanupStack::PushL( groupingInfo );
       
   704 
       
   705     // Read in all resource for this grouping scheme, perform checking and then analyze it
       
   706     HBufC* initialDigits = aResourceReader.ReadHBufCL();
       
   707     __ASSERT_ALWAYS( initialDigits, Panic( ENumberGroupingNoInitialDigitsInResource ) );
       
   708     CleanupStack::PushL( initialDigits );
       
   709 
       
   710     groupingInfo->iMinNumberOfDigits = aResourceReader.ReadInt8();
       
   711     groupingInfo->iMaxNumberOfDigits = aResourceReader.ReadInt8();
       
   712     __ASSERT_DEBUG(
       
   713         ( groupingInfo->iMaxNumberOfDigits == -1) ||
       
   714         ( groupingInfo->iMinNumberOfDigits <= groupingInfo->iMaxNumberOfDigits ),
       
   715         Panic( ENumberGroupingBadMinMaxDigitRangeInResource ) );
       
   716 
       
   717     // Read in formatting Pattern
       
   718     HBufC* formatPattern = aResourceReader.ReadHBufCL();
       
   719 
       
   720     if ( formatPattern ) // Does not have to be there
       
   721         {
       
   722         CleanupStack::PushL( formatPattern );
       
   723         TInt formatLength = formatPattern->Length();
       
   724         if ( formatLength > 0 )
       
   725             {
       
   726             // Obtain a wildcard version of the matching pattern in initialDigits.
       
   727             // This is used to check the supplied formatPattern for comformance to initialDigits
       
   728             HBufC* wildcardedMatchBuf = HBufC::NewLC( formatLength ); // Will not be longer than the search pattern
       
   729 
       
   730             TPtr wildcardedMatchPtr( wildcardedMatchBuf->Des() );
       
   731             // Get the example number using the latest search pattern only
       
   732             GetWildcardVersionOfMatchStringL( *initialDigits, KNumberGroupingWildcard, wildcardedMatchPtr );
       
   733 
       
   734             // Now parse the descriptor
       
   735             TBool trailingPossible(EFalse);
       
   736             ParseForAfterPositions(
       
   737                 *formatPattern, groupingInfo, wildcardedMatchPtr, aMaxExtraCharacters, trailingPossible );
       
   738 
       
   739             // Now parse the descriptor from the end if needed
       
   740             if ( trailingPossible )
       
   741                 ParseForBeforePosition( *formatPattern, groupingInfo, aMaxExtraCharacters );
       
   742 
       
   743             CleanupStack::PopAndDestroy( wildcardedMatchBuf );
       
   744             }
       
   745         CleanupStack::PopAndDestroy( formatPattern );
       
   746         } // End of if on formatPattern.Length
       
   747 
       
   748     User::LeaveIfError( aGroupingPatternsList.Append( initialDigits ) );
       
   749     CleanupStack::Pop( initialDigits );
       
   750 
       
   751     // Do not leave if the next one fails, but remove the last from the patterns list and then leave
       
   752     // This is done in case someone TRAPs. Otherwise neither of these lists would be used and their
       
   753     // mismatch would not be a problem
       
   754     if ( TInt err = iGroupingItemsList.Append(groupingInfo) != KErrNone )
       
   755         {
       
   756         // return value of Count will be at least 1, because we have just successfully gone through an Append
       
   757         aGroupingPatternsList.Remove( aGroupingPatternsList.Count() - 1 );
       
   758         // ownership is now mine again...
       
   759         delete initialDigits;
       
   760         // Need to delete groupingInfo, and make sure it is no longer on the cleanupstack
       
   761         CleanupStack::PopAndDestroy( groupingInfo );
       
   762         User::Leave(err);
       
   763         }
       
   764     else
       
   765         {
       
   766         CleanupStack::Pop( groupingInfo ); // Success. This object now not owned by the cleanupstack
       
   767         }        
       
   768     
       
   769     CleanupStack::Pop(&aGroupingPatternsList);
       
   770     }
       
   771 
       
   772 void CPNGNumberGrouping::ParseForAfterPositions(
       
   773     const TDesC& aFormatPattern,
       
   774     TPNGGroupingInfo* aGroupingInfo,
       
   775     const TDesC& aWildcardedMatchingPattern,
       
   776     TInt& aMaxExtraCharacters,
       
   777     TBool& trailingPossible ) const
       
   778     {
       
   779     TInt pos(0); // Keeps track of the position with which the next separator will be stored
       
   780     TInt formatLength = aFormatPattern.Length();
       
   781     for (TInt index = 0; index < formatLength; index++ )
       
   782         {
       
   783         // The format pattern is compared with the matching pattern.  The matching pattern may be
       
   784         // shorter than the format pattern, so by default a wildcard character is used.
       
   785         TText ch = aFormatPattern[index];
       
   786         TText matchingChar(KNumberGroupingWildcard); // default to expect is the wildcard character
       
   787         if ( pos < aWildcardedMatchingPattern.Length() ) // if still within the matching pattern
       
   788             matchingChar = aWildcardedMatchingPattern[pos];
       
   789         if ( ch == matchingChar )
       
   790             pos++; // not a separator. index where the next "after" marker goes
       
   791         else if ( ch == KNumberGroupingOneOrMoreCharactersToken )
       
   792             {
       
   793             // finish looking for "afterPositions". But there may be a "before" position in the
       
   794             // remainder, so set the flag
       
   795             trailingPossible = ETrue;
       
   796             break;
       
   797             }
       
   798         else
       
   799             {
       
   800             // Explicit prevention of any separator characters being valid phone numbers
       
   801 #ifdef _DEBUG
       
   802             if ( IsValidPhoneNumberCharacter( ch ) || ch == KNumberGroupingWildcard )
       
   803                 {
       
   804                 RDebug::Print(
       
   805                     _L("NumberGrouping: Illegal character or format mismatch in resource: initialDigits pattern= <%S> formatPattern=<%S>"),
       
   806                     &aWildcardedMatchingPattern, &aFormatPattern );
       
   807                 }
       
   808 #endif
       
   809             __ASSERT_DEBUG( !IsValidPhoneNumberCharacter( ch ), Panic( ENumberGroupingInvalidSeparatorCharacterInFormat ) );
       
   810             __ASSERT_DEBUG( ch != KNumberGroupingWildcard, Panic( ENumberGroupingMatchingPatternVersusFormatPatternMismatch ) );
       
   811             TPNGSeparator separator( pos, aFormatPattern[index]);
       
   812             aGroupingInfo->iAfterPositions.Append(separator);
       
   813             aMaxExtraCharacters++;
       
   814             }
       
   815         }
       
   816     }
       
   817 
       
   818 void CPNGNumberGrouping::ParseForBeforePosition(
       
   819     const TDesC& aFormatPattern,
       
   820     TPNGGroupingInfo* aGroupingInfo,
       
   821     TInt& aMaxExtraCharacters ) const
       
   822     {
       
   823     TInt pos=0;
       
   824     TInt formatLength = aFormatPattern.Length();
       
   825 
       
   826     for (TInt index = formatLength-1; index >=0; index-- )
       
   827         {
       
   828         TText ch = aFormatPattern[index];
       
   829         if ( ch == KNumberGroupingWildcard )
       
   830             pos++;
       
   831         else if ( ch == KNumberGroupingOneOrMoreCharactersToken )
       
   832             break;
       
   833         else
       
   834             {
       
   835             // Explicit prevention of any separator characters being valid phone numbers
       
   836 #ifdef _DEBUG
       
   837             if ( IsValidPhoneNumberCharacter( ch ) )
       
   838                 {
       
   839                 RDebug::Print(
       
   840                     _L("NumberGrouping: Illegal character in trailing part of format string in resource: formatPattern=<%S>"),
       
   841                     &aFormatPattern );
       
   842                 }
       
   843 #endif
       
   844             __ASSERT_DEBUG( !IsValidPhoneNumberCharacter( ch ),
       
   845                 Panic( ENumberGroupingInvalidSeparatorCharacterInFormat ) );
       
   846             TPNGSeparator separator( pos, ch );
       
   847             aGroupingInfo->iBeforePosition = separator;
       
   848             aMaxExtraCharacters++;
       
   849             break;
       
   850             }
       
   851         }
       
   852     }
       
   853 
       
   854 
       
   855 void CPNGNumberGrouping::SkipGroupingSchemeL( TResourceReader& aResourceReader ) const
       
   856     {
       
   857     HBufC* tempBuf;
       
   858     tempBuf = aResourceReader.ReadHBufCL();
       
   859     delete tempBuf;
       
   860     aResourceReader.Advance(2); // min and max characters
       
   861     tempBuf = aResourceReader.ReadHBufCL();
       
   862     delete tempBuf;
       
   863     }
       
   864 
       
   865 void CPNGNumberGrouping::GetWildcardVersionOfMatchStringL(
       
   866     const TDesC& aMatchString,
       
   867     TText aWildcard,
       
   868     TDes& aWildcardMatchString ) const
       
   869     {
       
   870     RPointerArray<TDesC> patternList;
       
   871     CleanupClosePushL(patternList);
       
   872 
       
   873     // Make a copy of the input string
       
   874     HBufC* matchString = aMatchString.AllocLC();
       
   875 
       
   876     User::LeaveIfError( patternList.Append(matchString) );// takes ownership
       
   877     CleanupStack::Pop( matchString );
       
   878 
       
   879     CRegularExpression* regExp = CRegularExpression::NewLC(&patternList);
       
   880 
       
   881     // Only 1 pattern fed in.  Access that pattern at index 0
       
   882     regExp->GetWildcardVersionOfPattern( 0 , aWildcard, aWildcardMatchString );
       
   883 
       
   884     CleanupStack::PopAndDestroy(regExp);
       
   885 
       
   886     // Delete the patterns list
       
   887     delete patternList[0];
       
   888     CleanupStack::PopAndDestroy();
       
   889     }
       
   890 
       
   891 
       
   892 EXPORT_C TBool CPNGNumberGrouping::IsCharacterInsertedByNumberGrouping(TInt aPos) const
       
   893     {
       
   894     TInt insertedCharacters = Length() - UnFormattedLength();
       
   895 
       
   896     if( insertedCharacters == 0 ) // no formatting was done
       
   897         return EFalse;
       
   898     else if ( aPos < ( insertedCharacters + LengthToGroup() ) )
       
   899         {
       
   900         return !IsValidPhoneNumberCharacter( (*iFormattedNumber)[aPos] );
       
   901         }
       
   902     else // aPos is pointing at or beyond index= LengthToGroup() + <chars inserted>; no formatting there
       
   903         return EFalse;
       
   904     }
       
   905 
       
   906 
       
   907 TBool CPNGNumberGrouping::IsValidPhoneNumberCharacter( TText aCharacter ) const
       
   908     {
       
   909     if ( ((TChar)aCharacter).IsDigit() )
       
   910         return ETrue;
       
   911 
       
   912     // Check through the list of additional valid phone number characters
       
   913     TInt numAdditionalChars = sizeof( KAdditionalPhoneNumberCharacters )/sizeof(TText);
       
   914 
       
   915     for (TInt index = 0; index < numAdditionalChars; index++)
       
   916         {
       
   917         if ( aCharacter == KAdditionalPhoneNumberCharacters[index] )
       
   918             return ETrue;
       
   919         }
       
   920 
       
   921     return EFalse;
       
   922     }
       
   923 
       
   924 EXPORT_C TBool CPNGNumberGrouping::IsChangedByGrouping() const
       
   925     {
       
   926     // The only way that grouping is effectively different is by making things longer
       
   927     return ( Length() > UnFormattedLength() );
       
   928     }
       
   929 
       
   930 TInt CPNGNumberGrouping::LengthToGroup() const
       
   931     {
       
   932 
       
   933     TPtrC ptr = iUnformattedNumber->Des();
       
   934     TInt lengthToGroup = ptr.Length();
       
   935 
       
   936     // Find the first non-digit
       
   937     for (TInt index = 0; index < ptr.Length(); index++)
       
   938         {
       
   939         TChar ch = TChar(ptr[index]);
       
   940         ch.Fold(TChar::EFoldDigits);
       
   941         if ( !( ch.IsDigit() ) )
       
   942             {
       
   943             lengthToGroup = index; // only characters BEFORE the character at index are grouped
       
   944             break;
       
   945             }
       
   946         }
       
   947 
       
   948     return lengthToGroup;
       
   949     }
       
   950 
       
   951 // End of File