textinput/ptienginev2/src/PtiSymbolList.cpp
changeset 0 eb1f2e154e89
child 9 e6a39382bb9c
equal deleted inserted replaced
-1:000000000000 0:eb1f2e154e89
       
     1 /*
       
     2 * Copyright (c) 2007 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:   Predective text input engine core local methods.
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 
       
    20 #include <f32file.h>
       
    21 #include <s32file.h>
       
    22 
       
    23 #include "PtiSymbolList.h"
       
    24 
       
    25 
       
    26 //=========== constant definition ==============
       
    27 _LIT( KSymbolMutexName,"PtiHwrSymbol_101F8610" );
       
    28 const TInt KUdmFileHeader = 0x20080808;
       
    29 const TInt KUdmFileVersion = 0x00001000;
       
    30 const TInt KInvalidPresetIndex = 0;
       
    31 
       
    32 
       
    33 void CSymbol::InternalizeL(RReadStream& /*aStream*/)
       
    34     {
       
    35     }
       
    36 
       
    37 void CSymbol::ExternalizeL(RWriteStream& aStream)
       
    38     {
       
    39     aStream.WriteInt32L( iHelpLine );
       
    40     aStream.WriteInt32L( iBaseLine );
       
    41     aStream.WriteInt32L( iRange.iScript );
       
    42     aStream.WriteInt32L( iRange.iRange );
       
    43     aStream.WriteUint32L( iPresetCode );
       
    44     if ( iSymbolName )
       
    45         {
       
    46         aStream.WriteInt32L( iSymbolName->Length() );
       
    47         aStream.WriteL( *iSymbolName, iSymbolName->Length() );
       
    48         }
       
    49     else
       
    50         {
       
    51         aStream.WriteInt32L( 0 );
       
    52         }
       
    53 
       
    54     aStream.WriteInt32L( iPointVectorLen );
       
    55     aStream.WriteL( reinterpret_cast< TUint8* >( iPointVector ), iPointVectorLen*sizeof( TPoint ) );
       
    56     	
       
    57     }
       
    58 
       
    59  CSymbol* CSymbol::NewL(RReadStream& aStream)
       
    60     {
       
    61     CSymbol* self  = new ( ELeave ) CSymbol();
       
    62     CleanupStack::PushL( self );	
       
    63     self->ConstructL( aStream );         
       
    64     CleanupStack::Pop( self );  
       
    65     return self; 	
       
    66     }
       
    67 
       
    68 void CSymbol::ConstructL(RReadStream& aStream)
       
    69     {
       
    70     iHelpLine = aStream.ReadInt32L();
       
    71     iBaseLine = aStream.ReadInt32L();
       
    72     iRange.iScript = aStream.ReadInt32L();
       
    73     iRange.iRange = aStream.ReadInt32L();
       
    74     iPresetCode = aStream.ReadUint32L();
       
    75     
       
    76     TInt symbolLen = aStream.ReadInt32L();
       
    77     iSymbolName = NULL;
       
    78     if ( symbolLen )
       
    79         {
       
    80         iSymbolName = HBufC::NewL( symbolLen );
       
    81         TPtr ptr = iSymbolName->Des();
       
    82         aStream.ReadL( ptr, symbolLen );
       
    83         }
       
    84 
       
    85     iPointVectorLen = aStream.ReadInt32L();
       
    86     iPointVector = new ( ELeave ) TPoint[iPointVectorLen];
       
    87     aStream.ReadL( reinterpret_cast< TUint8* >( iPointVector ), iPointVectorLen*sizeof( TPoint ) );
       
    88     }
       
    89 
       
    90 CSymbol::CSymbol()
       
    91     {
       
    92     iPresetCode = 0;
       
    93     }
       
    94 
       
    95  CSymbol* CSymbol::NewL(const TDesC& aText, const RArray<TPoint>& aModel, int aHelpLine, int aBaseLine,const THwrUdmRange& aRange)
       
    96     {	
       
    97     CSymbol* self =  new ( ELeave )CSymbol();
       
    98     CleanupStack::PushL( self );	
       
    99     self->ConstructL( aText, aModel, aHelpLine, aBaseLine, aRange );         
       
   100     CleanupStack::Pop( self );  
       
   101     return self; 
       
   102     }
       
   103 
       
   104 CSymbol::~CSymbol()
       
   105     {
       
   106     delete iSymbolName;
       
   107     delete [] iPointVector;
       
   108     }
       
   109 
       
   110 void CSymbol::ConstructL(const TDesC& aText, const RArray<TPoint>& aModel, int aHelpLine, int aBaseLine,const THwrUdmRange& aRange)
       
   111     {
       
   112     iHelpLine = aHelpLine;
       
   113     iBaseLine = aBaseLine;
       
   114     iRange = aRange;
       
   115     iSymbolName = aText.AllocL( );
       
   116     iPresetCode = KInvalidPresetIndex; 
       
   117     
       
   118     iPointVectorLen = aModel.Count();
       
   119     iPointVector = new ( ELeave ) TPoint[iPointVectorLen];
       
   120     memcpy( iPointVector, &(aModel[0]), iPointVectorLen*sizeof( TPoint ) );
       
   121     }
       
   122 
       
   123 TInt CSymbol::SymbolOrderDescending(const CSymbol& aFirst, const CSymbol& aSecond)
       
   124     {
       
   125     return aFirst.iSymbolName->Compare( *(aSecond.iSymbolName ) );
       
   126     }
       
   127 
       
   128 TBool CSymbol::Match ( const THwrUdmRange& aRange )
       
   129     {
       
   130     if ( aRange.iScript !=  EPtiHwrScriptAny )
       
   131         {
       
   132         if ( aRange.iScript != iRange.iScript )
       
   133             {
       
   134             return EFalse;
       
   135             }
       
   136         }
       
   137     
       
   138     if ( aRange.iRange != EPtiHwrRangeAny )
       
   139         {
       
   140         if ( aRange.iRange != iRange.iRange )
       
   141             {
       
   142             return EFalse;
       
   143             }
       
   144         }
       
   145     
       
   146     return ETrue;    
       
   147     }
       
   148 
       
   149 CSymbolList* CSymbolList::NewL(const TDesC& aFilePath, CSymbolList* aPresetList )
       
   150     {
       
   151     CSymbolList* self  =  new ( ELeave ) CSymbolList( aPresetList );
       
   152     CleanupStack::PushL( self );	
       
   153     self->ConstructL( aFilePath );         
       
   154     CleanupStack::Pop( self );  
       
   155     return self;                
       
   156     }
       
   157     
       
   158 void CSymbolList::SetSymbolModelL(const TDesC& aText, const RArray<TPoint>& aModel, TInt aHelpLine , TInt aBaseLine ,const THwrUdmRange& aRange )
       
   159     {
       
   160     if( !aText.Length() || !aModel.Count() )
       
   161         User::Leave( KErrGeneral );
       
   162     if ( CheckSymbolModel( aText , aRange ) )
       
   163         {
       
   164         User::Leave( KErrAlreadyExists );
       
   165         }
       
   166     
       
   167     CSymbol* symbol = CSymbol::NewL( aText, aModel, aHelpLine, aBaseLine, aRange );       
       
   168     CleanupStack::PushL( symbol );
       
   169     TLinearOrder<CSymbol> order( CSymbol::SymbolOrderDescending );
       
   170     // using order insertion later
       
   171     // iSymbolList.InsertInOrderL( symbol, order );
       
   172     iSymbolList.AppendL( symbol );
       
   173     CleanupStack::Pop( symbol );
       
   174     ExternalizeL();
       
   175     }
       
   176 
       
   177 TBool CSymbolList::CheckSymbolModel(const TDesC& aChar ,const THwrUdmRange& aRange )
       
   178     {
       
   179     TInt idx = -1;
       
   180     return GetSymbolIndex( aChar, idx, aRange ) == KErrNone ? ETrue : EFalse ;
       
   181     }
       
   182 
       
   183 void CSymbolList::GetSymbolModelL(const TDesC& aChar, RArray<TPoint>& aModel ,TUint& aUnicode, const THwrUdmRange& aRange )
       
   184     {
       
   185     TInt idx = -1;
       
   186     if ( GetSymbolIndex( aChar, idx, aRange ) != KErrNone )
       
   187         {
       
   188         User::Leave( KErrNotFound );
       
   189         }
       
   190     // decides which list the idx belongs to   
       
   191     CSymbol* symbol = NULL; 
       
   192     if ( idx >= iSymbolList.Count() && iPresetModels )
       
   193         {
       
   194         symbol = iPresetModels->iSymbolList[ idx - iSymbolList.Count() ];
       
   195         }
       
   196     else
       
   197         {
       
   198         symbol = iSymbolList[idx];
       
   199         }
       
   200         
       
   201     aUnicode = symbol->iPresetCode;
       
   202     aModel.Reset();
       
   203     for ( int i = 0; i < symbol->iPointVectorLen; i++ )
       
   204         {
       
   205         aModel.AppendL( symbol->iPointVector[i] );    
       
   206         }    
       
   207     }
       
   208 
       
   209 void CSymbolList::DeleteSymbolModelL(const TDesC& aChar ,const THwrUdmRange& aRange )
       
   210     {
       
   211     TInt idx = -1;
       
   212     if ( GetSymbolIndex( aChar, idx, aRange ) != KErrNone )
       
   213         {
       
   214         User::Leave( KErrNotFound );
       
   215         }
       
   216     if ( idx < iSymbolList.Count() )
       
   217         {
       
   218         CSymbol* symbol = iSymbolList[idx]; 
       
   219         iSymbolList.Remove( idx );
       
   220         delete symbol;
       
   221         ExternalizeL();
       
   222         }
       
   223     else if ( iPresetModels )
       
   224         {
       
   225         // modify preset models
       
   226         TInt presetIdx = idx -iSymbolList.Count();
       
   227         CSymbol& symbol = *iPresetModels->iSymbolList[presetIdx];
       
   228         delete symbol.iSymbolName;
       
   229         symbol.iSymbolName = NULL;
       
   230         iPresetModels->ExternalizeL();
       
   231         }
       
   232     }
       
   233 
       
   234 void CSymbolList::GetModelTextListL(RPointerArray<HBufC>& aList ,const THwrUdmRange& /*aRange*/ )
       
   235     {
       
   236     for ( int i = 0; i < iSymbolList.Count(); i++ )
       
   237         {
       
   238         aList.AppendL( (*iSymbolList[i]->iSymbolName).AllocL() );
       
   239         }
       
   240         
       
   241     // then get preset shortcut models if have
       
   242     if ( iPresetModels )
       
   243         {
       
   244         for ( int i = 0; i < iPresetModels->iSymbolList.Count(); i++ )
       
   245             {
       
   246             // if the preset model is assigned to a shourtcut, then append it to the list
       
   247             CSymbol* symbol = iPresetModels->iSymbolList[i];
       
   248             if ( symbol->iSymbolName && symbol->iPresetCode )
       
   249                 {
       
   250                 aList.AppendL( symbol->iSymbolName->AllocL() );
       
   251                 }
       
   252             }
       
   253         }
       
   254     }
       
   255 
       
   256 void CSymbolList::ChangeSymbolTextL(const TDesC& aOldText, const TDesC& aNewText ,const THwrUdmRange& aRange )
       
   257     {
       
   258     TInt idx = -1;
       
   259     if ( GetSymbolIndex( aOldText, idx, aRange ) != KErrNone )
       
   260         {
       
   261         User::Leave( KErrNotFound );
       
   262         } 
       
   263         
       
   264     if ( aOldText.Compare( aNewText ) == 0 )
       
   265         {
       
   266         return ;  
       
   267         }
       
   268         
       
   269     if ( CheckSymbolModel( aNewText, aRange ) )
       
   270         {
       
   271         User::Leave( KErrAlreadyExists );
       
   272         }  
       
   273     if ( idx < iSymbolList.Count() )
       
   274         {
       
   275         CSymbol& symbol = *iSymbolList[idx];
       
   276         delete symbol.iSymbolName;
       
   277         symbol.iSymbolName = aNewText.AllocL();   
       
   278         ExternalizeL();
       
   279         }
       
   280     else if ( iPresetModels )
       
   281         {
       
   282         // modify preset models
       
   283         TInt presetIdx = idx -iSymbolList.Count();
       
   284         
       
   285         CSymbol& symbol = *iPresetModels->iSymbolList[presetIdx];
       
   286         delete symbol.iSymbolName;
       
   287         symbol.iSymbolName = aNewText.AllocL(); ;
       
   288         
       
   289         iPresetModels->ExternalizeL();
       
   290         }
       
   291     }
       
   292     
       
   293 void CSymbolList::GetModelIndexListL( RArray<TInt>& aList, const THwrUdmRange& aRange)
       
   294     {
       
   295     aList.Reset();
       
   296     for ( int i = 0; i < iSymbolList.Count(); i++ )
       
   297         {
       
   298         if ( iSymbolList[i]->Match( aRange ) )
       
   299             {
       
   300             aList.AppendL( i );
       
   301             }
       
   302         }
       
   303     
       
   304     // append the symbols to the array    
       
   305     if ( iPresetModels )
       
   306         {
       
   307         for ( int i = 0; i < iPresetModels->iSymbolList.Count(); i++ )
       
   308             {
       
   309             if ( iPresetModels->iSymbolList[i]->iSymbolName &&
       
   310                     iPresetModels->iSymbolList[i]->Match( aRange  ) )
       
   311                 {
       
   312                 aList.AppendL( i + iSymbolList.Count() );
       
   313                 }
       
   314             }
       
   315         }
       
   316     }
       
   317 
       
   318 void CSymbolList::InternalizeL(const TDesC& /*aFile*/)
       
   319     {
       
   320     // Read the data file and construct the object
       
   321     iMutex.Wait();
       
   322     CleanupStack::PushL(TCleanupItem(SignalMutex, &iMutex));
       
   323     RFile readFile;
       
   324     TInt errCode = readFile.Open( iRfs, *iFilePath, EFileRead  );
       
   325     CleanupClosePushL( readFile );
       
   326         
       
   327     if ( errCode == KErrNone  )
       
   328         {
       
   329         RFileReadStream readStream( readFile );
       
   330         CleanupClosePushL( readStream );
       
   331         
       
   332         // check file Type&Version
       
   333         TInt fileType = readStream.ReadInt32L();
       
   334         TInt fileVersion = readStream.ReadInt32L();
       
   335         if ( fileType != KUdmFileHeader || fileVersion != KUdmFileVersion )
       
   336             {
       
   337             User::Leave( KErrGeneral );
       
   338             }
       
   339         
       
   340         // construct each CSymbol object and add to array
       
   341         TInt symbolNO = readStream.ReadInt32L();
       
   342         for ( int i = 0; i < symbolNO; i++ )  
       
   343             {  
       
   344             CSymbol* symbol = CSymbol::NewL( readStream );
       
   345             iSymbolList.AppendL( symbol );
       
   346             }
       
   347 
       
   348         // check wether the file is valid.
       
   349         if ( symbolNO != iSymbolList.Count() )
       
   350             {
       
   351             User::Leave( KErrGeneral );    
       
   352             }    
       
   353         CleanupStack::PopAndDestroy( &readStream );  
       
   354         }
       
   355     CleanupStack::PopAndDestroy( &readFile );  
       
   356     CleanupStack::PopAndDestroy(); // TCleanupItem(SignalMutex, &iMutex)
       
   357     }
       
   358 
       
   359 void CSymbolList::ExternalizeL()
       
   360     {
       
   361     // write a temp file and use replace.
       
   362     iMutex.Wait();
       
   363     CleanupStack::PushL(TCleanupItem(SignalMutex, &iMutex));
       
   364     
       
   365     RFile fileTemp;
       
   366     CleanupClosePushL( fileTemp );
       
   367     
       
   368     //HBufC* tempFile = HBufC::NewLC( iFilePath->Length() + 3 );
       
   369     //TPtr tempFilePtr( tempFile->Des() );
       
   370     //tempFilePtr.Copy( *iFilePath );
       
   371     //tempFilePtr.Append( KTempPathFix );
       
   372     
       
   373 
       
   374     TParse fileParse;
       
   375     fileParse.Set( *iFilePath, NULL, NULL );
       
   376         
       
   377     TFileName tempName;
       
   378     iRfs.MkDirAll( fileParse.DriveAndPath() );
       
   379     User::LeaveIfError ( fileTemp.Temp( iRfs, fileParse.DriveAndPath(), tempName, EFileWrite ) );
       
   380     
       
   381     RFileWriteStream writeStream( fileTemp );
       
   382     CleanupClosePushL( writeStream );
       
   383 
       
   384     // write file type&version
       
   385     writeStream.WriteInt32L( KUdmFileHeader );
       
   386     writeStream.WriteInt32L( KUdmFileVersion );
       
   387     
       
   388     writeStream.WriteInt32L( iSymbolList.Count() );
       
   389     for ( int i = 0; i < iSymbolList.Count(); i++ )  
       
   390         {  
       
   391         iSymbolList[i]->ExternalizeL( writeStream );
       
   392         }
       
   393     writeStream.CommitL();
       
   394     CleanupStack::PopAndDestroy( &writeStream );
       
   395     CleanupStack::PopAndDestroy( &fileTemp );
       
   396     
       
   397     iRfs.Delete( *iFilePath ) ;
       
   398     User::LeaveIfError( iRfs.Rename( tempName ,*iFilePath  ) );
       
   399     
       
   400     CleanupStack::PopAndDestroy(); // TCleanupItem(SignalMutex, &iMutex)
       
   401     }
       
   402 
       
   403 CSymbolList::CSymbolList( CSymbolList* aPresetList )
       
   404     {
       
   405     iPresetModels = aPresetList;
       
   406     iFilePath = NULL;
       
   407     }
       
   408 
       
   409 void CSymbolList::ConstructL(const TDesC& aFilePath)
       
   410     {
       
   411     User::LeaveIfError( iRfs.Connect() );
       
   412     iFilePath = aFilePath.AllocL();   
       
   413     
       
   414     // create mutex
       
   415     TInt error( KErrNotFound );
       
   416     while( error == KErrNotFound )
       
   417          {
       
   418          error = iMutex.CreateGlobal( KSymbolMutexName );
       
   419          if( error != KErrAlreadyExists )
       
   420              {
       
   421              break;
       
   422              }
       
   423          error = iMutex.OpenGlobal( KSymbolMutexName );
       
   424          }
       
   425      User::LeaveIfError( error );
       
   426       
       
   427     InternalizeL( aFilePath );
       
   428     }
       
   429 
       
   430 CSymbolList::~CSymbolList()
       
   431     {
       
   432     delete iFilePath;
       
   433     for ( int i = 0; i < iSymbolList.Count(); i++ )
       
   434         {
       
   435         delete iSymbolList[i];
       
   436         }
       
   437     iSymbolList.Close();    
       
   438     iRfs.Close();
       
   439     iMutex.Close();
       
   440     }
       
   441 
       
   442 TInt CSymbolList::GetSymbolIndex(const TDesC& aText, TInt& aIndex, const THwrUdmRange& aRange )
       
   443     {
       
   444     // using quick search later.
       
   445     for ( int i = 0; i < iSymbolList.Count(); i++ )
       
   446         {
       
   447         if ( iSymbolList[i]->iSymbolName->Compare( aText ) == 0 && iSymbolList[i]->Match( aRange ))
       
   448             {
       
   449             aIndex = i;
       
   450             return KErrNone;    
       
   451             }   
       
   452         }
       
   453 
       
   454     // then find in preset model        
       
   455     if ( iPresetModels )
       
   456         {
       
   457         for ( int i = 0; i < iPresetModels->iSymbolList.Count(); i++ )
       
   458             {
       
   459             CSymbol& symbol = *iPresetModels->iSymbolList[i];
       
   460             // not assigned
       
   461             if ( !symbol.iSymbolName )
       
   462                 {
       
   463                 continue;
       
   464                 }
       
   465                 
       
   466             if ( symbol.iSymbolName->Compare( aText ) == 0 && symbol.Match( aRange ))
       
   467                 {
       
   468                 aIndex = iSymbolList.Count() + i;
       
   469                 return KErrNone;    
       
   470                 }   
       
   471             }
       
   472         }
       
   473     
       
   474     return KErrNotFound;    
       
   475     }
       
   476 
       
   477 TInt CSymbolList::GetSymbolDataRef( TInt aIndex, TPtrC& aSymbolName,RArray<TPoint>& aModel, TInt& aHelpLine, TInt& aBaseLine )
       
   478     {
       
   479 
       
   480     CSymbol* symbol = NULL; 
       
   481     if ( aIndex >= 0 && aIndex < iSymbolList.Count() )
       
   482         {
       
   483         symbol = iSymbolList[aIndex];
       
   484         }
       
   485     else if ( iPresetModels && aIndex >= iSymbolList.Count() 
       
   486                     && aIndex <   iSymbolList.Count() + iPresetModels->iSymbolList.Count() )
       
   487         {
       
   488         symbol = iPresetModels->iSymbolList[ aIndex - iSymbolList.Count() ];
       
   489         }
       
   490     else
       
   491         {
       
   492         return KErrNotFound;
       
   493         }
       
   494         
       
   495     aModel.Reset();    
       
   496     aModel = RArray<TPoint>( sizeof(TPoint), symbol->iPointVector, symbol->iPointVectorLen );
       
   497     aHelpLine = symbol->iHelpLine;
       
   498     aBaseLine = symbol->iBaseLine;
       
   499     aSymbolName.Set( *symbol->iSymbolName );
       
   500 
       
   501     return KErrNone;  
       
   502     }
       
   503 
       
   504 
       
   505 void CSymbolList::SavePresetShortcutL( TUint aUnicode, const TDesC& aShortcut ) 
       
   506     {
       
   507     User::LeaveIfNull( iPresetModels );
       
   508     
       
   509     for ( int i = 0; i < iPresetModels->iSymbolList.Count(); i ++ )
       
   510         {
       
   511         CSymbol* sym = iPresetModels->iSymbolList[i];
       
   512         if ( aUnicode == sym->iPresetCode )
       
   513             {
       
   514             if ( sym-> iSymbolName )
       
   515                 {
       
   516                 delete sym->iSymbolName;
       
   517                 sym->iSymbolName = NULL;
       
   518                 }
       
   519                 
       
   520             if ( aShortcut.Length() > 0 )
       
   521                 {
       
   522                 sym->iSymbolName = aShortcut.AllocL();
       
   523                 }
       
   524             iPresetModels->ExternalizeL();    
       
   525             return;
       
   526             }
       
   527         }
       
   528         
       
   529     User::Leave( KErrNotFound );
       
   530     }
       
   531 
       
   532 void CSymbolList::GetAllPresetSymbolsL( RArray<TUint>& aPresets )
       
   533     {
       
   534     User::LeaveIfNull( iPresetModels );
       
   535     
       
   536     for ( int i = 0; i < iPresetModels->iSymbolList.Count(); i ++ )
       
   537         {
       
   538         aPresets.AppendL( iPresetModels->iSymbolList[i]->iPresetCode );
       
   539         }
       
   540     }
       
   541 
       
   542 void CSymbolList::GetPresetSymbolByUnicodeL( TUint aUnicode, RArray<TPoint>& aModel, TDes& aShortcut )
       
   543     {
       
   544     User::LeaveIfNull( iPresetModels );
       
   545     
       
   546     for ( int i = 0; i < iPresetModels->iSymbolList.Count(); i ++ )
       
   547         {
       
   548         CSymbol* sym = iPresetModels->iSymbolList[i];
       
   549         if ( aUnicode == sym->iPresetCode )
       
   550             {
       
   551             aShortcut = KNullDesC;
       
   552             if ( sym->iSymbolName )
       
   553                 {
       
   554                 aShortcut.Copy( *sym->iSymbolName );
       
   555                 }
       
   556             
       
   557             for ( int i = 0; i < sym->iPointVectorLen; i++ )
       
   558                 {
       
   559                 aModel.AppendL( sym->iPointVector[i] );    
       
   560                 }    
       
   561             return;
       
   562             }
       
   563         }
       
   564     User::Leave( KErrNotFound );    
       
   565     }
       
   566     
       
   567 void CSymbolList::SignalMutex(TAny* aMutex)
       
   568    {
       
   569    STATIC_CAST( RMutex*, aMutex )->Signal();
       
   570    }
       
   571 
       
   572 
       
   573 
       
   574 
       
   575 
       
   576 
       
   577