phonebookui/Phonebook2/MMCExtension/src/CPmuCopyToMmcCmd.cpp
changeset 0 e686773b3f54
equal deleted inserted replaced
-1:000000000000 0:e686773b3f54
       
     1 /*
       
     2 * Copyright (c) 2005-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:  Phonebook 2 MMC UI extension copy to MMC command.
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 #include "cpmucopytommccmd.h"
       
    20 
       
    21 // Phonebook 2
       
    22 #include <mpbk2contactuicontrol.h>
       
    23 #include <mpbk2applicationservices.h>
       
    24 #include <mpbk2appui.h>
       
    25 #include <mpbk2contactnameformatter.h>
       
    26 #include <mpbk2commandobserver.h>
       
    27 #include <pbk2processdecoratorfactory.h>
       
    28 #include <mpbk2processdecorator.h>
       
    29 #include <tpbk2copycontactsresults.h>
       
    30 #include <pbk2mmcuires.rsg>
       
    31 
       
    32 // Virtual Phonebook
       
    33 #include <mvpbkcontactoperationbase.h>
       
    34 #include <mvpbkstorecontact.h>
       
    35 #include <cvpbkvcardeng.h>
       
    36 #include <cvpbkcontactmanager.h>
       
    37 #include <cvpbkcontactlinkarray.h>
       
    38 
       
    39 // System includes
       
    40 #include <pathinfo.h>
       
    41 #include <stringloader.h>
       
    42 #include <aknnotewrappers.h>
       
    43 
       
    44 /// Unnamed namespace for local definitions
       
    45 namespace {
       
    46 
       
    47 _LIT( KPbk2VCardFileExtension, ".vcf" );
       
    48 _LIT( KInvalidFileNameChars, "?*<>/\"|\\:" );
       
    49 _LIT( KNumberFormat, "%d" );
       
    50 _LIT( KOpeningParenthesis, "(" );
       
    51 _LIT( KClosingParenthesis, ")" );
       
    52 _LIT( KZero, "0" );
       
    53 const TInt KOneContact = 1;
       
    54 
       
    55 /**
       
    56  * Removes invalid characters from given descriptor.
       
    57  *
       
    58  * @param aPtr  The descriptor to inspect.
       
    59  */
       
    60 void RemoveInvalidCharacters( TPtr& aPtr )
       
    61     {
       
    62     for ( TInt i = aPtr.Length() - 1; i >= 0; --i )
       
    63         {
       
    64         TChar ch = aPtr[i];
       
    65         if ( KInvalidFileNameChars().Locate( ch ) != KErrNotFound )
       
    66             {
       
    67             aPtr.Delete( i, 1 ); // ch is not a valid character
       
    68             }
       
    69         }
       
    70     }
       
    71 
       
    72 enum TPmuCopyToMmcCmdState
       
    73     {
       
    74     EPmuCopyToMmcCmdStartProcess,
       
    75     EPmuCopyToMmcCmdDeleteOldEntries,
       
    76     EPmuCopyToMmcCmdRun,
       
    77     EPmuCopyToMmcCmdComplete,
       
    78     EPmuCopyToMmcCmdCancel
       
    79     };
       
    80 
       
    81 #ifdef _DEBUG
       
    82 
       
    83 enum TPanicCode
       
    84     {
       
    85     EPanic_CopyNextL_OOB = 1,
       
    86     EPanic_OperationComplete_PreCond,
       
    87     EPanic_ShowResultsL_PreCond
       
    88     };
       
    89 
       
    90 void Panic( TPanicCode aPanic )
       
    91     {
       
    92     _LIT( KPanicCat, "CPmuCopyToMmcCmd" );
       
    93     User::Panic( KPanicCat(), aPanic );
       
    94     }
       
    95 
       
    96 #endif // _DEBUG
       
    97 
       
    98 } /// namespace
       
    99 
       
   100 // --------------------------------------------------------------------------
       
   101 // CPmuCopyToMmcCmd::CPmuCopyToMmcCmd
       
   102 // --------------------------------------------------------------------------
       
   103 //
       
   104 CPmuCopyToMmcCmd::CPmuCopyToMmcCmd( MPbk2ContactUiControl& aUiControl ) :
       
   105         CActive( EPriorityStandard ),
       
   106         iUiControl( &aUiControl )
       
   107     {
       
   108     CActiveScheduler::Add( this );
       
   109     }
       
   110 
       
   111 // --------------------------------------------------------------------------
       
   112 // CPmuCopyToMmcCmd::~CPmuCopyToMmcCmd
       
   113 // --------------------------------------------------------------------------
       
   114 //
       
   115 CPmuCopyToMmcCmd::~CPmuCopyToMmcCmd()
       
   116     {
       
   117     Cancel();
       
   118 
       
   119     if ( iUiControl )
       
   120         {
       
   121         iUiControl->RegisterCommand( NULL );
       
   122         }
       
   123 
       
   124     iWriteStream.Close();
       
   125     delete iFileMan;
       
   126     delete iDecorator;
       
   127     delete iStoreContact;
       
   128     delete iPreviousContact;
       
   129     delete iContactLinks;
       
   130     delete iVCardEngine;
       
   131     }
       
   132 
       
   133 // --------------------------------------------------------------------------
       
   134 // CPmuCopyToMmcCmd::NewL
       
   135 // --------------------------------------------------------------------------
       
   136 //
       
   137 CPmuCopyToMmcCmd* CPmuCopyToMmcCmd::NewL( MPbk2ContactUiControl& aUiControl )
       
   138     {
       
   139     CPmuCopyToMmcCmd* self = new ( ELeave ) CPmuCopyToMmcCmd( aUiControl );
       
   140     CleanupStack::PushL( self );
       
   141     self->ConstructL();
       
   142     CleanupStack::Pop( self );
       
   143     return self;
       
   144     }
       
   145 
       
   146 // --------------------------------------------------------------------------
       
   147 // CPmuCopyToMmcCmd::ConstructL
       
   148 // --------------------------------------------------------------------------
       
   149 //
       
   150 inline void CPmuCopyToMmcCmd::ConstructL()
       
   151     {
       
   152     iVCardEngine = CVPbkVCardEng::NewL
       
   153         ( Phonebook2::Pbk2AppUi()->ApplicationServices().ContactManager() );
       
   154 
       
   155     iContactLinks = iUiControl->SelectedContactsOrFocusedContactL();
       
   156 
       
   157     iDecorator = Pbk2ProcessDecoratorFactory::CreateProgressDialogDecoratorL
       
   158         ( R_PMU_COPY_PROGRESS_NOTE, ETrue );
       
   159     iDecorator->SetObserver( *this );
       
   160 
       
   161     iUiControl->RegisterCommand( this );
       
   162 
       
   163     // set the default contacts path
       
   164     iContactsPath = PathInfo::MemoryCardContactsPath();
       
   165     }
       
   166 
       
   167 // --------------------------------------------------------------------------
       
   168 // CPmuCopyToMmcCmd::RunL
       
   169 // --------------------------------------------------------------------------
       
   170 //
       
   171 void CPmuCopyToMmcCmd::RunL()
       
   172     {
       
   173     switch( iState )
       
   174         {
       
   175         case EPmuCopyToMmcCmdStartProcess:
       
   176             {
       
   177             iDecorator->ProcessStartedL(iContactLinks->Count());
       
   178 
       
   179             if ( iDeleteOldEntries )
       
   180                 {
       
   181                 iState = EPmuCopyToMmcCmdDeleteOldEntries;
       
   182                 IssueRequest();
       
   183                 }
       
   184             else
       
   185                 {
       
   186                 Phonebook2::Pbk2AppUi()->ApplicationServices().ContactManager().
       
   187                     FsSession().MkDirAll( iContactsPath );
       
   188 
       
   189                 iState = EPmuCopyToMmcCmdRun;
       
   190                 IssueRequest();
       
   191                 }
       
   192             break;
       
   193             }
       
   194         case EPmuCopyToMmcCmdDeleteOldEntries:
       
   195             {
       
   196             DeleteOldEntriesL();
       
   197             Phonebook2::Pbk2AppUi()->ApplicationServices().ContactManager().
       
   198                 FsSession().MkDirAll( iContactsPath );
       
   199             break;
       
   200             }
       
   201         case EPmuCopyToMmcCmdRun:
       
   202             {
       
   203             if (iFileMan)
       
   204                 {
       
   205                 delete iFileMan;
       
   206                 iFileMan = NULL;
       
   207                 }
       
   208 
       
   209             if (iStoreContact)
       
   210                 {
       
   211                 HBufC* fileName = CreateFileNameLC(*iStoreContact);
       
   212                 User::LeaveIfError( 
       
   213                     iWriteStream.Create(
       
   214                         Phonebook2::Pbk2AppUi()->ApplicationServices().
       
   215                             ContactManager().FsSession(),
       
   216                                 *fileName, EFileWrite ));
       
   217                     
       
   218                 iExportOperation = iVCardEngine->ExportVCardForSyncL(
       
   219                     iWriteStream, *iStoreContact, *this );
       
   220                 CleanupStack::PopAndDestroy(fileName);
       
   221                 }
       
   222             else if (iCurrentIndex < iContactLinks->Count())
       
   223                 {
       
   224                 CopyNextL();
       
   225                 }
       
   226             else
       
   227                 {
       
   228                 iState = EPmuCopyToMmcCmdComplete;
       
   229                 IssueRequest();
       
   230                 }
       
   231             break;
       
   232             }
       
   233         case EPmuCopyToMmcCmdComplete:
       
   234             {
       
   235             // Copy complete, decorator calls processdismissed
       
   236             iDecorator->ProcessStopped();
       
   237             break;
       
   238             }
       
   239         default:
       
   240             {
       
   241             }
       
   242         }
       
   243     }
       
   244 
       
   245 // --------------------------------------------------------------------------
       
   246 // CPmuCopyToMmcCmd::DoCancel
       
   247 // --------------------------------------------------------------------------
       
   248 //
       
   249 void CPmuCopyToMmcCmd::DoCancel()
       
   250     {
       
   251     iState = EPmuCopyToMmcCmdCancel;
       
   252     
       
   253     delete iRetrieveOperation;
       
   254     iRetrieveOperation = NULL;
       
   255     delete iExportOperation;
       
   256     iExportOperation = NULL;
       
   257     }
       
   258 
       
   259 // --------------------------------------------------------------------------
       
   260 // CPmuCopyToMmcCmd::RunError
       
   261 // --------------------------------------------------------------------------
       
   262 //
       
   263 TInt CPmuCopyToMmcCmd::RunError( TInt /*aError*/ )
       
   264     {
       
   265     delete iRetrieveOperation;
       
   266     iRetrieveOperation = NULL;
       
   267     delete iExportOperation;
       
   268     iExportOperation = NULL;
       
   269 
       
   270     if ( iDecorator )
       
   271         {
       
   272         iDecorator->ProcessStopped();
       
   273         }
       
   274 
       
   275     return KErrNone;
       
   276     }
       
   277 
       
   278 // --------------------------------------------------------------------------
       
   279 // CPmuCopyToMmcCmd::ExecuteLD
       
   280 // --------------------------------------------------------------------------
       
   281 //
       
   282 void CPmuCopyToMmcCmd::ExecuteLD()
       
   283     {
       
   284     CleanupStack::PushL( this );
       
   285 
       
   286     ShowConfirmationQueryL();
       
   287 
       
   288     CleanupStack::Pop( this );
       
   289     }
       
   290 
       
   291 // --------------------------------------------------------------------------
       
   292 // CPmuCopyToMmcCmd::AddObserver
       
   293 // --------------------------------------------------------------------------
       
   294 //
       
   295 void CPmuCopyToMmcCmd::AddObserver( MPbk2CommandObserver& aObserver )
       
   296     {
       
   297     iCommandObserver = &aObserver;
       
   298     }
       
   299 
       
   300 // --------------------------------------------------------------------------
       
   301 // CPmuCopyToMmcCmd::ResetUiControl
       
   302 // --------------------------------------------------------------------------
       
   303 //
       
   304 void CPmuCopyToMmcCmd::ResetUiControl( MPbk2ContactUiControl& aUiControl )
       
   305     {
       
   306     if ( iUiControl == &aUiControl )
       
   307         {
       
   308         iUiControl = NULL;
       
   309         }
       
   310     }
       
   311 
       
   312 // --------------------------------------------------------------------------
       
   313 // CPmuCopyToMmcCmd::ProcessDismissed
       
   314 // --------------------------------------------------------------------------
       
   315 //
       
   316 void CPmuCopyToMmcCmd::ProcessDismissed( TInt /*aCancelCode*/ )
       
   317     {
       
   318     Cancel();
       
   319     delete iRetrieveOperation;
       
   320     iRetrieveOperation = NULL;
       
   321     delete iExportOperation;
       
   322     iExportOperation = NULL;
       
   323 
       
   324     // It is a not big deal if result note is not shown to user
       
   325     TRAP_IGNORE( ShowResultsL() );
       
   326     if ( iUiControl )
       
   327         {
       
   328         iUiControl->UpdateAfterCommandExecution();
       
   329         }
       
   330 
       
   331     iCommandObserver->CommandFinished( *this );
       
   332     }
       
   333 
       
   334 // --------------------------------------------------------------------------
       
   335 // CPmuCopyToMmcCmd::VPbkSingleContactOperationComplete
       
   336 // --------------------------------------------------------------------------
       
   337 //
       
   338 void CPmuCopyToMmcCmd::VPbkSingleContactOperationComplete
       
   339         ( MVPbkContactOperationBase& aOperation,
       
   340           MVPbkStoreContact* aContact )
       
   341     {
       
   342     __ASSERT_DEBUG( iDecorator, Panic( EPanic_OperationComplete_PreCond ));
       
   343 
       
   344     if ( &aOperation == iRetrieveOperation )
       
   345         {
       
   346         delete iRetrieveOperation;
       
   347         iRetrieveOperation = NULL;
       
   348 
       
   349         iStoreContact = aContact;
       
   350         }
       
   351     else if ( &aOperation == iExportOperation )
       
   352         {
       
   353         delete iExportOperation;
       
   354         iExportOperation = NULL;
       
   355 
       
   356         // After the copying take the contact pointer,
       
   357         // because if this was last contact we need it in result note
       
   358         delete iPreviousContact;
       
   359         iPreviousContact = iStoreContact; // iPreviousContact takes ownership
       
   360         iStoreContact = NULL;
       
   361 
       
   362         iWriteStream.Close();
       
   363 
       
   364         // Copy next and update progressbar
       
   365         ++iCurrentIndex;
       
   366         ++iCountOfContacts;
       
   367         iDecorator->ProcessAdvance( 1 );
       
   368         }
       
   369     IssueRequest();
       
   370     }
       
   371 
       
   372 // --------------------------------------------------------------------------
       
   373 // CPmuCopyToMmcCmd::VPbkSingleContactOperationFailed
       
   374 // --------------------------------------------------------------------------
       
   375 //
       
   376 void CPmuCopyToMmcCmd::VPbkSingleContactOperationFailed
       
   377         ( MVPbkContactOperationBase& aOperation, TInt /*aError*/ )
       
   378     {
       
   379     if (&aOperation == iRetrieveOperation)
       
   380         {
       
   381         delete iRetrieveOperation;
       
   382         iRetrieveOperation = NULL;
       
   383         }
       
   384     else if (&aOperation == iExportOperation)
       
   385         {
       
   386         delete iExportOperation;
       
   387         iExportOperation = NULL;
       
   388         delete iStoreContact;
       
   389         iStoreContact = NULL;
       
   390         }
       
   391 
       
   392     // Copy next
       
   393     ++iCurrentIndex;
       
   394     IssueRequest();
       
   395     }
       
   396 
       
   397 // --------------------------------------------------------------------------
       
   398 // CPmuCopyToMmcCmd::NotifyFileManStarted
       
   399 // --------------------------------------------------------------------------
       
   400 //    
       
   401 MFileManObserver::TControl CPmuCopyToMmcCmd::NotifyFileManStarted()
       
   402     {
       
   403     if ( iState == EPmuCopyToMmcCmdCancel )
       
   404         {
       
   405         return MFileManObserver::EAbort;
       
   406         }
       
   407     return MFileManObserver::EContinue;
       
   408     }
       
   409     
       
   410 // --------------------------------------------------------------------------
       
   411 // CPmuCopyToMmcCmd::NotifyFileManOperation
       
   412 // --------------------------------------------------------------------------
       
   413 //
       
   414 MFileManObserver::TControl CPmuCopyToMmcCmd::NotifyFileManOperation()
       
   415     {
       
   416     if ( iState == EPmuCopyToMmcCmdCancel )
       
   417         {
       
   418         return MFileManObserver::EAbort;
       
   419         }
       
   420     return MFileManObserver::EContinue;
       
   421     }
       
   422 
       
   423 // --------------------------------------------------------------------------
       
   424 // CPmuCopyToMmcCmd::CopyNextL
       
   425 // --------------------------------------------------------------------------
       
   426 //
       
   427 void CPmuCopyToMmcCmd::CopyNextL()
       
   428     {
       
   429     __ASSERT_DEBUG( iContactLinks->Count() > iCurrentIndex,
       
   430          EPanic_CopyNextL_OOB );
       
   431     iRetrieveOperation = Phonebook2::Pbk2AppUi()->ApplicationServices().
       
   432             ContactManager().RetrieveContactL
       
   433                 ( iContactLinks->At( iCurrentIndex ), *this );
       
   434     }
       
   435 
       
   436 // --------------------------------------------------------------------------
       
   437 // CPmuCopyToMmcCmd::IssueRequest
       
   438 // --------------------------------------------------------------------------
       
   439 //
       
   440 void CPmuCopyToMmcCmd::IssueRequest()
       
   441     {
       
   442     if ( !IsActive() )
       
   443         {
       
   444         TRequestStatus* status = &iStatus;
       
   445         User::RequestComplete( status, KErrNone );
       
   446         SetActive();
       
   447         }
       
   448     }
       
   449 
       
   450 // --------------------------------------------------------------------------
       
   451 // CPmuCopyToMmcCmd::CreateFileNameLC
       
   452 // --------------------------------------------------------------------------
       
   453 //
       
   454 HBufC* CPmuCopyToMmcCmd::CreateFileNameLC
       
   455         ( const MVPbkStoreContact& aContact ) const
       
   456     {
       
   457     HBufC* title = Phonebook2::Pbk2AppUi()->ApplicationServices().
       
   458         NameFormatter().GetContactTitleL( aContact.Fields(), 0 );
       
   459 
       
   460     CleanupStack::PushL(title);
       
   461     TPtr titlePtr = title->Des();
       
   462 
       
   463     RemoveInvalidCharacters(titlePtr);
       
   464 
       
   465     TBool fileNameFound = EFalse;
       
   466     TInt number = 0;
       
   467     TFileName fileName;
       
   468     while (!fileNameFound)
       
   469         {
       
   470         fileName.Zero();
       
   471         fileName.Append( iContactsPath );
       
   472         fileName.Append(titlePtr);
       
   473 
       
   474         if (number > 0)
       
   475             {
       
   476             fileName.Append(KOpeningParenthesis);
       
   477             if (number < 10)
       
   478                 {
       
   479                 fileName.Append(KZero);
       
   480                 }
       
   481             fileName.AppendFormat(KNumberFormat, number);
       
   482             fileName.Append(KClosingParenthesis);
       
   483             }
       
   484 
       
   485         fileName.Append(KPbk2VCardFileExtension);
       
   486 
       
   487         TEntry entry;
       
   488         TInt err = Phonebook2::Pbk2AppUi()->ApplicationServices().
       
   489             ContactManager().FsSession().Entry(fileName, entry);
       
   490         if (err == KErrNotFound)
       
   491             {
       
   492             fileNameFound = ETrue;
       
   493             }
       
   494         else
       
   495             {
       
   496             User::LeaveIfError( err );
       
   497             ++number;
       
   498             }
       
   499         }
       
   500     CleanupStack::PopAndDestroy(title);
       
   501 
       
   502     return fileName.AllocLC();
       
   503     }
       
   504 
       
   505 // --------------------------------------------------------------------------
       
   506 // CPmuCopyToMmcCmd::DeleteOldEntriesL
       
   507 // --------------------------------------------------------------------------
       
   508 //
       
   509 void CPmuCopyToMmcCmd::DeleteOldEntriesL()
       
   510     {
       
   511     iFileMan = CFileMan::NewL
       
   512         (Phonebook2::Pbk2AppUi()->ApplicationServices().ContactManager().
       
   513             FsSession(), this);
       
   514     User::LeaveIfError( iFileMan->Delete(
       
   515             iContactsPath,
       
   516             CFileMan::ERecurse, iStatus ) );
       
   517     iState = EPmuCopyToMmcCmdRun;
       
   518     SetActive();
       
   519     }
       
   520 
       
   521 // --------------------------------------------------------------------------
       
   522 // CPmuCopyToMmcCmd::ShowConfirmationQueryL
       
   523 // --------------------------------------------------------------------------
       
   524 //
       
   525 void CPmuCopyToMmcCmd::ShowConfirmationQueryL()
       
   526     {
       
   527     HBufC* prompt = StringLoader::LoadLC(R_QTN_PHOB_QUERY_COPY_MMC_ALL);
       
   528     CAknQueryDialog* dlg = CAknQueryDialog::NewL();
       
   529 
       
   530     TInt result = dlg->ExecuteLD
       
   531         (R_PHONEBOOK2_CONFIRM_DELETE_EXISTING_CONTACTS, *prompt);
       
   532     CleanupStack::PopAndDestroy(prompt);
       
   533     iDeleteOldEntries = (result != 0);
       
   534 
       
   535     iState = EPmuCopyToMmcCmdStartProcess;
       
   536     IssueRequest();
       
   537     }
       
   538 
       
   539 // --------------------------------------------------------------------------
       
   540 // CPmuCopyToMmcCmd::ShowMemorySelectionDialogL
       
   541 // --------------------------------------------------------------------------
       
   542 //
       
   543 TBool CPmuCopyToMmcCmd::ShowMemorySelectionDialogL()
       
   544     {
       
   545     return EFalse;
       
   546     }
       
   547 
       
   548 // --------------------------------------------------------------------------
       
   549 // CPmuCopyToMmcCmd::ShowResultsL
       
   550 // --------------------------------------------------------------------------
       
   551 //
       
   552 void CPmuCopyToMmcCmd::ShowResultsL()
       
   553     {
       
   554     const TInt contactCount = iContactLinks->Count();
       
   555     if (contactCount == KOneContact)
       
   556         {
       
   557         HBufC* title = NULL;
       
   558 
       
   559         // If we do not have iPreviousContact, then probably coping is failed
       
   560         if ( iPreviousContact )
       
   561             {
       
   562             title = Phonebook2::Pbk2AppUi()->ApplicationServices().
       
   563                 NameFormatter().GetContactTitleL( iPreviousContact->Fields(),
       
   564                     MPbk2ContactNameFormatter::EPreserveLeadingSpaces);
       
   565             CleanupStack::PushL(title);
       
   566             }
       
   567         else if ( iStoreContact )
       
   568             {
       
   569             // If we still have iStoreContact, then retrieve is atleast
       
   570             // completed and we can show note.
       
   571             title = Phonebook2::Pbk2AppUi()->ApplicationServices().
       
   572                 NameFormatter().GetContactTitleL( iStoreContact->Fields(),
       
   573                 MPbk2ContactNameFormatter::EPreserveLeadingSpaces );
       
   574             CleanupStack::PushL(title);
       
   575             }
       
   576 
       
   577         if ( title )
       
   578             {
       
   579             TPbk2CopyContactsResults results( iCountOfContacts, *title );
       
   580             results.SetOneContactCopiedTextRes
       
   581                 ( R_QTN_PBCOP_NOTE_CONTACT_COPIED_PB2 );
       
   582             results.SetOneContactNotCopiedTextRes
       
   583                 ( R_QTN_PBCOP_NOTE_ENTRY_NOT_COPIED );
       
   584             results.ShowNoteL();
       
   585 
       
   586             CleanupStack::PopAndDestroy( title );
       
   587             }
       
   588         }
       
   589     else
       
   590         {
       
   591         TPbk2CopyContactsResults results(
       
   592             iCountOfContacts, iContactLinks->Count());
       
   593         results.ShowNoteL();
       
   594         }
       
   595     }
       
   596 
       
   597 // End of File