diff -r 5b6f26637ad3 -r f4a778e096c2 phonebookengines/VirtualPhonebook/VPbkCntModel/src/CFindViewBase.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/phonebookengines/VirtualPhonebook/VPbkCntModel/src/CFindViewBase.cpp Wed Sep 01 12:29:52 2010 +0100 @@ -0,0 +1,815 @@ +/* +* Copyright (c) 2002-2007 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Contacts Model store filtered contact view implementation. +* +*/ + + +// INCLUDES +#include "CFindViewBase.h" + +// VPbkCntModel +#include "CContactStore.h" +#include "CRefineView.h" +#include "CViewBase.h" +#include "CContactLink.h" +#include "CContactBookmark.h" + +// VPbkEng +#include +#include +#include +#include +#include +#include +#include + +// System includes +#include + +namespace{ + #ifdef _DEBUG + enum TPanic + { + EPreCond_SetFindStringsL = 1 + }; + + void Panic(TPanic aPanic) + { + _LIT(KPanicCat, "CFindViewBase"); + User::Panic(KPanicCat, aPanic); + } + #endif // _DEBUG +} //namespace + +namespace VPbkCntModel { + +// CONSTANTS +const TInt KGranularity( 4 ); + +// -------------------------------------------------------------------------- +// CCompareView::CCompareView +// -------------------------------------------------------------------------- +// +CCompareView::CCompareView( const CContactDatabase& aDb ) : + CContactViewBase( aDb ) + { + } + +// -------------------------------------------------------------------------- +// CCompareView::CompareFieldsL +// -------------------------------------------------------------------------- +// +TInt CCompareView::CompareFieldsL( const ::CViewContact &aFirst, + const ::CViewContact &aSecond ) + { + return CContactViewBase::CompareFieldsL( aFirst, aSecond ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::CFindViewBase +// -------------------------------------------------------------------------- +// +CFindViewBase::CFindViewBase( + MParentViewForFiltering& aParentView, + CViewBase& aBaseView, + TBool aOwnsContacts ) : + iParentView( aParentView ), + iBaseView( aBaseView ), + iOwnsContacts( aOwnsContacts ), + iDestroyed( NULL ) + { + } + +// -------------------------------------------------------------------------- +// CFindViewBase::BaseConstructL +// -------------------------------------------------------------------------- +// +void CFindViewBase::BaseConstructL( const MDesCArray& aFindStrings, + MVPbkContactFindPolicy& aFindPolicy, + MVPbkContactViewObserver& aExternalViewObserver ) + { + iObserverOp = + CVPbkAsyncObjectOperation::NewL(); + iFilterObsOp = + CVPbkAsyncObjectOperation::NewL(); + + iFindStrings = new(ELeave)CDesCArrayFlat( KGranularity ); + SetFindStringsL( aFindStrings ); + + // Set contact find policy + iContactFindPolicy = &aFindPolicy; + + // Add the external observer + iObservers.AppendL( &aExternalViewObserver ); + + //Create field type list + iFieldTypeRefsList = CVPbkFieldTypeRefsList::NewL(); + + iViewContact = CViewContact::NewL( iBaseView, SortOrder() ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::~CFindViewBase +// -------------------------------------------------------------------------- +// +CFindViewBase::~CFindViewBase() + { + delete iObserverOp; + delete iFilterObsOp; + ResetContacts(); + iMatchedContacts.Close(); + iParentView.RemoveFilteringObserver( *this ); + iObservers.Close(); + iFilteringObservers.Close(); + + delete iViewContact; + delete iFieldTypeRefsList; + delete iFindStrings; + if ( iDestroyed ) + { + *iDestroyed = ETrue; + } + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ActivateContactMatchL +// -------------------------------------------------------------------------- +// +void CFindViewBase::ActivateContactMatchL() + { + // Remove first if this called second time when updating. + iParentView.RemoveFilteringObserver( *this ); + // This will cause an asynchrnous view event to this view from the + // parent view. + iParentView.AddFilteringObserverL( *this ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ParentObject +// -------------------------------------------------------------------------- +// +MVPbkObjectHierarchy& CFindViewBase::ParentObject() const + { + return iParentView.ParentObject(); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::RefreshL +// -------------------------------------------------------------------------- +// +void CFindViewBase::RefreshL() + { + iParentView.RefreshL(); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ContactCountL +// -------------------------------------------------------------------------- +// +TInt CFindViewBase::ContactCountL() const + { + return iMatchedContacts.Count(); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ContactAtL +// -------------------------------------------------------------------------- +// +const MVPbkViewContact& CFindViewBase::ContactAtL( + TInt aIndex ) const + { + __ASSERT_ALWAYS( aIndex >= 0, + VPbkError::Panic( VPbkError::EInvalidContactIndex ) ); + + if ( aIndex >= ContactCountL() ) + { + User::Leave( KErrArgument ); + } + + const ::CViewContact& viewContact = *iMatchedContacts[ aIndex ]; + iViewContact->SetViewContact(viewContact); + return *iViewContact; + } + +// -------------------------------------------------------------------------- +// CFindViewBase::CreateLinkLC +// -------------------------------------------------------------------------- +// +MVPbkContactLink* CFindViewBase::CreateLinkLC( + TInt aIndex ) const + { + __ASSERT_ALWAYS( aIndex >= 0, + VPbkError::Panic( VPbkError::EInvalidContactIndex ) ); + + if ( aIndex >= ContactCountL() ) + { + User::Leave( KErrArgument ); + } + + TContactItemId contactId = iMatchedContacts[aIndex]->Id(); + return CContactLink::NewLC( iBaseView.Store(), contactId ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::IndexOfLinkL +// -------------------------------------------------------------------------- +// +TInt CFindViewBase::IndexOfLinkL( + const MVPbkContactLink& aContactLink ) const + { + if (&aContactLink.ContactStore() == &iParentView.ContactStore()) + { + const CContactLink& link = + static_cast(aContactLink); + const TInt count( iMatchedContacts.Count() ); + for ( TInt i(0); i < count; ++i ) + { + if ( iMatchedContacts[ i ]->Id() == link.ContactId() ) + { + return i; + } + } + } + + return KErrNotFound; + } + +// -------------------------------------------------------------------------- +// CFindViewBase::Type +// -------------------------------------------------------------------------- +// +TVPbkContactViewType CFindViewBase::Type() const + { + return iParentView.Type(); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ChangeSortOrderL +// -------------------------------------------------------------------------- +// +void CFindViewBase::ChangeSortOrderL(const MVPbkFieldTypeList& aSortOrder) + { + iParentView.ChangeSortOrderL( aSortOrder ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::SortOrder +// -------------------------------------------------------------------------- +// +const MVPbkFieldTypeList& CFindViewBase::SortOrder() const + { + return iParentView.SortOrder(); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::AddObserverL +// -------------------------------------------------------------------------- +// +void CFindViewBase::AddObserverL( MVPbkContactViewObserver& aObserver ) + { + CVPbkAsyncObjectCallback* callback = + VPbkEngUtils::CreateAsyncObjectCallbackLC( + *this, + &CFindViewBase::DoAddObserver, + &CFindViewBase::DoAddObserverError, + aObserver); + iObserverOp->CallbackL( callback ); + CleanupStack::Pop( callback ); + + // Insert to first position because events are send in reverse order. + iObservers.InsertL( &aObserver, 0 ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::RemoveObserver +// -------------------------------------------------------------------------- +// +void CFindViewBase::RemoveObserver(MVPbkContactViewObserver& aObserver) + { + const TInt index = iObservers.Find( &aObserver ); + if ( index != KErrNotFound ) + { + iObservers.Remove( index ); + } + } + +// -------------------------------------------------------------------------- +// CFindViewBase::MatchContactStore +// -------------------------------------------------------------------------- +// +TBool CFindViewBase::MatchContactStore(const TDesC& aContactStoreUri) const + { + return iParentView.MatchContactStore( aContactStoreUri ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::MatchContactStoreDomain +// -------------------------------------------------------------------------- +// +TBool CFindViewBase::MatchContactStoreDomain(const TDesC& aContactStoreDomain) const + { + return iParentView.MatchContactStoreDomain( aContactStoreDomain ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::CreateBookmarkLC +// -------------------------------------------------------------------------- +// +MVPbkContactBookmark* CFindViewBase::CreateBookmarkLC( + TInt aIndex ) const + { + __ASSERT_ALWAYS( aIndex >= 0, + VPbkError::Panic( VPbkError::EInvalidContactIndex ) ); + + TContactItemId contactId = iMatchedContacts[aIndex]->Id(); + // Link implements also the bookmark interface in this store + return CContactBookmark::NewLC( contactId, iBaseView.Store() ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::IndexOfBookmarkL +// -------------------------------------------------------------------------- +// +TInt CFindViewBase::IndexOfBookmarkL( + const MVPbkContactBookmark& aContactBookmark ) const + { + // Bookmark is implemented in this store + const CContactBookmark* bookmark = + dynamic_cast(&aContactBookmark); + if (bookmark && + &bookmark->ContactStore() == &iParentView.ContactStore() ) + { + const TInt count( iMatchedContacts.Count() ); + for ( TInt i(0); i < count; ++i ) + { + if ( iMatchedContacts[ i ]->Id() == bookmark->ContactId() ) + { + return i; + } + } + } + return KErrNotFound; + } + +// -------------------------------------------------------------------------- +// CFindViewBase::AddFilteringObserverL +// -------------------------------------------------------------------------- +// +void CFindViewBase::AddFilteringObserverL( + MFilteredViewSupportObserver& aObserver ) + { + // Insert observer in callback function. That ensures that the observer + // will always get the event asynchronously. + + CVPbkAsyncObjectCallback* callback = + VPbkEngUtils::CreateAsyncObjectCallbackLC( + *this, + &CFindViewBase::DoAddFilteringObserverL, + &CFindViewBase::DoAddFilteringObserverError, + aObserver); + + iFilterObsOp->CallbackL( callback ); + CleanupStack::Pop( callback ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::RemoveFilteringObserver +// -------------------------------------------------------------------------- +// +void CFindViewBase::RemoveFilteringObserver( + MFilteredViewSupportObserver& aObserver ) + { + iFilterObsOp->CancelCallback( &aObserver ); + const TInt index( iFilteringObservers.Find( &aObserver ) ); + if ( index != KErrNotFound ) + { + iFilteringObservers.Remove( index ); + } + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ViewFiltering +// -------------------------------------------------------------------------- +// +MVPbkContactViewFiltering* CFindViewBase::ViewFiltering() + { + // This view supports further filtering. + return this; + } + +// -------------------------------------------------------------------------- +// CFindViewBase::CreateFilteredViewLC +// -------------------------------------------------------------------------- +// +MVPbkContactViewBase* CFindViewBase::CreateFilteredViewLC( + MVPbkContactViewObserver& aObserver, + const MDesCArray& aFindWords, + const MVPbkContactBookmarkCollection* /*aAlwaysIncludedContacts*/ ) + { + // NOTE: aAlwaysIncludedContacts is commented because in VPbkCntModel + // filtered views are linked. CFindView->CRefineView->CRefineView etc. + // Only the CFindView saves the always included contacts and they + // are used also by all CRefineView instances through + // MAlwaysIncludedContacts interface. + CRefineView* refineView = CRefineView::NewLC( aFindWords, *this, + iBaseView, aObserver, *iContactFindPolicy, + *this ); + refineView->ActivateContactMatchL(); + return refineView; + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ContactViewReady +// -------------------------------------------------------------------------- +// +void CFindViewBase::ContactViewReady( + MVPbkContactViewBase& /*aView*/ ) + { + SendViewStateEventToObservers(); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ContactViewUnavailable +// -------------------------------------------------------------------------- +// +void CFindViewBase::ContactViewUnavailable( + MVPbkContactViewBase& /*aView*/ ) + { + SendViewStateEventToObservers(); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ContactAddedToView +// -------------------------------------------------------------------------- +// +void CFindViewBase::ContactAddedToView( + MVPbkContactViewBase& aView, + TInt aIndex, + const MVPbkContactLink& aContactLink ) + { + TRAPD( error, HandleContactAddedToViewL( aView, aIndex, aContactLink ) ); + if ( error != KErrNone ) + { + ContactViewError( aView, error, EFalse ); + } + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ContactRemovedFromView +// -------------------------------------------------------------------------- +// +void CFindViewBase::ContactRemovedFromView( + MVPbkContactViewBase& aView, + TInt aIndex, + const MVPbkContactLink& aContactLink ) + { + TRAPD( error, HandleContactRemovedFromViewL( aView, aIndex, aContactLink ) ); + if ( error != KErrNone ) + { + ContactViewError( aView, error, EFalse ); + } + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ContactViewError +// -------------------------------------------------------------------------- +// +void CFindViewBase::ContactViewError( MVPbkContactViewBase& /*aView*/, + TInt aError, TBool aErrorNotified ) + { + iViewReady = EFalse; + ResetContacts(); + SendContactViewErrorEvent( aError, aErrorNotified ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ContactViewReadyForFiltering +// -------------------------------------------------------------------------- +// +void CFindViewBase::ContactViewReadyForFiltering( + MParentViewForFiltering& aView ) + { + TRAPD( error, DoContactViewReadyForFilteringL( aView ) ); + if ( error != KErrNone ) + { + ContactViewError( aView, error, EFalse ); + } + else + { + ContactViewReady( aView ); + } + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ContactViewUnavailableForFiltering +// -------------------------------------------------------------------------- +// +void CFindViewBase::ContactViewUnavailableForFiltering( + MParentViewForFiltering& /*aView*/ ) + { + iViewReady = EFalse; + ResetContacts(); + VPbkEng::SendViewEventToObservers( *this, iFilteringObservers, + &MFilteredViewSupportObserver::ContactViewUnavailableForFiltering, + &MVPbkContactViewObserver::ContactViewError ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::IsMatchL +// -------------------------------------------------------------------------- +// +TBool CFindViewBase::IsMatchL( const MVPbkViewContact& aViewContact ) + { + return iContactFindPolicy->MatchContactNameL( *iFindStrings, + aViewContact ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::FindStrings +// -------------------------------------------------------------------------- +// +const MDesCArray& CFindViewBase::FindStrings() const + { + return *iFindStrings; + } + +// ------------------------------------------------------------------------- +// CFindViewBase::SetFindStringsL +// -------------------------------------------------------------------------- +// +void CFindViewBase::SetFindStringsL( const MDesCArray& aFindStrings ) + { + __ASSERT_DEBUG( iFindStrings, Panic( EPreCond_SetFindStringsL ) ); + + iFindStrings->Reset(); + const TInt count = aFindStrings.MdcaCount(); + for ( TInt i = 0; i < count; ++i ) + { + iFindStrings->AppendL( aFindStrings.MdcaPoint( i ) ); + } + } + +// ------------------------------------------------------------------------- +// CFindViewBase::SendViewStateEventToObservers +// -------------------------------------------------------------------------- +// +void CFindViewBase::SendViewStateEventToObservers() + { + // Cancel any new AddObserverL callbacks to avoid duplicate event + // sending. + iObserverOp->Purge(); + + void (MVPbkContactViewObserver::*notifyFunc)(MVPbkContactViewBase&); + notifyFunc = &MVPbkContactViewObserver::ContactViewReady; + + if ( !iViewReady ) + { + notifyFunc = &MVPbkContactViewObserver::ContactViewUnavailable; + } + + VPbkEng::SendViewEventToObservers( *this, iObservers, + notifyFunc, &MVPbkContactViewObserver::ContactViewError ); + VPbkEng::SendViewEventToObservers( *this, iFilteringObservers, + notifyFunc, &MVPbkContactViewObserver::ContactViewError ); + } + +// ------------------------------------------------------------------------- +// CFindViewBase::DoContactViewReadyForFilteringL +// -------------------------------------------------------------------------- +// +void CFindViewBase::DoContactViewReadyForFilteringL( + MVPbkContactViewBase& /*aView*/ ) + { + ContactMatchL(); + iViewReady = ETrue; + // Send ContactViewReadyForFiltering event so that other views in + // stack can rebuild themselves before external observer are notified. + VPbkEng::SendViewEventToObservers( *this, iFilteringObservers, + &MFilteredViewSupportObserver::ContactViewReadyForFiltering, + &MVPbkContactViewObserver::ContactViewError ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::HandleContactAddedToViewL +// -------------------------------------------------------------------------- +// +void CFindViewBase::HandleContactAddedToViewL( MVPbkContactViewBase& aView, + TInt aIndex, const MVPbkContactLink& aContactLink ) + { + if ( &iParentView == &aView ) + { + // Let sub class do the addition if needed + DoContactAddedToViewL( aView, aIndex, aContactLink, + iMatchedContacts ); + + // Get the index of the new contact in this view. + TInt index( IndexOfLinkL( aContactLink ) ); + if ( index != KErrNotFound ) + { + VPbkEng::SendViewEventToObservers( *this, index, aContactLink, + iObservers, + &MVPbkContactViewObserver::ContactAddedToView, + &MVPbkContactViewObserver::ContactViewError ); + VPbkEng::SendViewEventToObservers( *this, index, aContactLink, + iFilteringObservers, + &MVPbkContactViewObserver::ContactAddedToView, + &MVPbkContactViewObserver::ContactViewError ); + } + } + } + +// -------------------------------------------------------------------------- +// CFindViewBase::HandleContactRemovedFromViewL +// -------------------------------------------------------------------------- +// +void CFindViewBase::HandleContactRemovedFromViewL( + MVPbkContactViewBase& aView, TInt /*aIndex*/, + const MVPbkContactLink& aContactLink ) + { + if ( &iParentView == &aView ) + { + TInt index = KErrNotFound; + + CCntModelViewContact* removedContact = NULL; + CViewContact* viewContact = CViewContact::NewL( iBaseView, SortOrder() ); + CleanupStack::PushL( viewContact ); + const TInt count( iMatchedContacts.Count() ); + for ( TInt i= 0; i < count && !removedContact; ++i ) + { + CCntModelViewContact* contact = iMatchedContacts[i]; + viewContact->SetViewContact( *contact ); + + // Filter away the removed contact + if ( aContactLink.RefersTo( *viewContact ) ) + { + // Remove contact from the array + iMatchedContacts.Remove( i ); + // Save the removed index for the observers + index = i; + // At this point nobody owns removedContact. + removedContact = contact; + } + } + CleanupStack::PopAndDestroy( viewContact ); + TBool takeCareOfOwnership = (removedContact && iOwnsContacts); + + // Notice: we can not delete the contact instance before + // all observers have received the event. This is because + // in case of CRefineView, iMatchedContacts contains references + // to CCntModelViewContact instances. If we delete the contact + // before notifying observer then the there will be invalid + // pointer. + if ( takeCareOfOwnership ) + { + CleanupStack::PushL( removedContact ); + } + + if ( index != KErrNotFound ) + { + VPbkEng::SendViewEventToObservers( *this, index, aContactLink, + iObservers, + &MVPbkContactViewObserver::ContactRemovedFromView, + &MVPbkContactViewObserver::ContactViewError ); + VPbkEng::SendViewEventToObservers( *this, index, aContactLink, + iFilteringObservers, + &MVPbkContactViewObserver::ContactRemovedFromView, + &MVPbkContactViewObserver::ContactViewError ); + } + + if ( takeCareOfOwnership) + { + // After all observers have received the Removed -event, it's + // safe to actually destroy the contact. + CleanupStack::PopAndDestroy( removedContact ); + } + } + } + +// -------------------------------------------------------------------------- +// CFindViewBase::DoAddObserver +// -------------------------------------------------------------------------- +// +void CFindViewBase::DoAddObserver( MVPbkContactViewObserver& aObserver ) + { + if (iViewReady) + { + // If this view is ready and there was no error, + // tell it to the observer + aObserver.ContactViewReady( *this ); + } + else + { + aObserver.ContactViewUnavailable( *this ); + } + } + +// -------------------------------------------------------------------------- +// CFindViewBase::DoAddObserverError +// -------------------------------------------------------------------------- +// +void CFindViewBase::DoAddObserverError( + MVPbkContactViewObserver& /*aObserver*/, TInt /*aError*/ ) + { + // DoAddObserver doesn't leave so nothing to implement here + } + +// -------------------------------------------------------------------------- +// CFindViewBase::DoAddFilteringObserverL +// -------------------------------------------------------------------------- +// +void CFindViewBase::DoAddFilteringObserverL( + MFilteredViewSupportObserver& aObserver ) + { + // Insert to first position because events are send in reverse order. + iFilteringObservers.InsertL( &aObserver, 0 ); + + if (iViewReady) + { + aObserver.ContactViewReadyForFiltering( *this ); + aObserver.ContactViewReady( *this ); + } + else + { + aObserver.ContactViewUnavailableForFiltering( *this ); + aObserver.ContactViewUnavailable( *this ); + } + } + +// -------------------------------------------------------------------------- +// CFindViewBase::DoAddFilteringObserverError +// -------------------------------------------------------------------------- +// +void CFindViewBase::DoAddFilteringObserverError( + MFilteredViewSupportObserver& aObserver, TInt aError ) + { + // See DoAddFilteringObserverL. If it leaves then adding the aObserver + // failed. + aObserver.ContactViewError( *this, aError, EFalse ); + } + +// -------------------------------------------------------------------------- +// CFindViewBase::ContactMatchL +// -------------------------------------------------------------------------- +// +void CFindViewBase::ContactMatchL() + { + // Reset current content + ResetContacts(); + // Subclass matches + MatchL( iMatchedContacts ); + } + + +// -------------------------------------------------------------------------- +// CFindViewBase::ResetContacts +// -------------------------------------------------------------------------- +// +void CFindViewBase::ResetContacts() + { + if ( iOwnsContacts ) + { + iMatchedContacts.ResetAndDestroy(); + } + else + { + iMatchedContacts.Reset(); + } + } + +// -------------------------------------------------------------------------- +// CFindViewBase::SendContactViewErrorEvent +// -------------------------------------------------------------------------- +// +void CFindViewBase::SendContactViewErrorEvent( TInt aError, + TBool aErrorNotified ) + { + TBool destroy = EFalse; + iDestroyed = &destroy; + + // Send first to external observers... + VPbkEng::SendEventToObserversWhenNotDestroyed( *this, aError, aErrorNotified, iObservers, + &MVPbkContactViewObserver::ContactViewError, destroy ); + // ...then to internal. This ensures that events come first from lower + // level find view. + VPbkEng::SendEventToObserversWhenNotDestroyed( *this, aError, aErrorNotified, + iFilteringObservers, &MVPbkContactViewObserver::ContactViewError, destroy ); + + if ( !destroy ) + { + iDestroyed = NULL; + } + } +} // namespace VPbkCntModel +// End of File