phonebookengines/VirtualPhonebook/VPbkEng/src/CVPbkContactLinkArray.cpp
changeset 0 e686773b3f54
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/phonebookengines/VirtualPhonebook/VPbkEng/src/CVPbkContactLinkArray.cpp	Tue Feb 02 10:12:17 2010 +0200
@@ -0,0 +1,883 @@
+/*
+* Copyright (c) 2005-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:  An array for contact links.
+*
+*/
+
+
+// INCLUDES
+#include "CVPbkContactLinkArray.h"
+
+#include "MVPbkContactStoreLoader.h"
+#include <MVPbkContactLink.h>
+#include <MVPbkStreamable.h>
+#include <TVPbkContactStoreUriPtr.h>
+#include <MVPbkContactStore.h>
+#include <MVPbkContactStoreProperties.h>
+#include <VPbkError.h>
+#include <MVPbkContactLinkPacking.h>
+#include <MVPbkContactStoreList.h>
+
+#include <s32mem.h>
+#include <badesca.h>
+#include <bamdesca.h>
+
+// packaged array static data
+namespace KLinkArrayStatic
+    {
+    /* streaming version 1 doesnt contain any optimizations */
+    const TUint8 KVPbkPackagedLinkArrayVersionV1 = 1;
+    /* streaming version 2 is optimized for memory usage */
+    const TUint8 KVPbkPackagedLinkArrayVersionV2 = 2;    
+    }
+
+namespace KLinkArrayExternalizeSizes
+    {
+    const TInt KVersionNumberSize = 1;
+    const TInt KStoreUriCountSize = 2;
+    const TInt KContactLinkCountSize = 4;
+    const TInt KUriLengthSize = 2;
+    const TInt KUriIndexSize = 2;
+    const TInt KInternalPackedSize = 4;
+    }
+
+namespace
+    {
+    const TUint8 KUriMarked = 1;
+    const TUint8 KUriNotMarked = 0;
+    const TInt KUriArrayGranularity = 4;
+
+#ifdef _DEBUG
+    enum TPanic
+        {
+        EPreCond_IsUriMarked,
+        EPreCond_MarkUri,
+        EPreCond_RemoveUnmarkedUris,
+        EPreCond_AppendStoreL
+        };
+        
+    void Panic( TPanic aPanic )
+        {
+        _LIT(KPanicCat, "CVPbkContactLinkArray");
+        User::Panic( KPanicCat, aPanic );
+        }
+#endif // _DEBUG
+    }
+
+/**
+ * A helper class for sharing contact store URI descriptors.
+ */
+NONSHARABLE_CLASS(CVPbkContactLinkArray::CStoreUris)
+        :   public CBase
+    {
+    public:
+        static CStoreUris* NewL();
+        ~CStoreUris();
+        
+        /**
+         * Appends the URI into array if if not appended earlier
+         */
+        void AppendIfNotFoundL( const MVPbkContactStore& aStore );
+        /**
+         * Appends the URI from the link if not appended earlier
+         */
+        void AppendIfNotFoundL( const MVPbkContactLink& aContactLink );
+        /**
+         * Appends empty URI to array
+         */
+        void AppendEmptyL();
+        /**
+         * @return the number of URIs
+         */
+        inline TInt Count() const;
+        /**
+         * @return a URI in the given Index
+         */
+        inline TPtrC At( TInt aIndex ) const;
+        /**
+         * @return the index of the store URI
+         */
+        inline TInt Find( const TDesC& aUri ) const;
+        /**
+         * @return the index of the URI or KErrNotFound
+         */
+        inline TInt Find( const MVPbkContactStore& aStore );
+        /**
+         * Destroys all URIs
+         */
+        void ResetAndDestroy();
+        /**
+         * @return ETrue if the URI is marked
+         * @param aUri the URI to be checked
+         */
+        inline TBool IsUriMarked( const MVPbkContactStore& aStore );
+        /**
+         * Marks the URI
+         * @param aUri the URI to be marked
+         */
+        void MarkUri( const MVPbkContactStore& aStore );
+        /**
+         * Unmarks all
+         */
+        void UnmarkAllUris();
+        /**
+         * Removes URIs that have no mark
+         */
+        void RemoveUnmarkedUris();
+        
+    private:
+        CStoreUris();
+        void ConstructL();
+        void AppendStoreL( const MVPbkContactStore* aStore );
+        
+    private:
+        /// Ref: An array for stores that are used to retrieve URIs
+        RPointerArray<MVPbkContactStore> iStores;
+        /// Own: an array of marks. This must be in balance with iStores
+        RArray<TUint8> iMarkedUris;
+    };
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::CStoreUris
+// ---------------------------------------------------------------------------
+//
+inline CVPbkContactLinkArray::CStoreUris::CStoreUris()
+        :   iStores( KUriArrayGranularity ), 
+            iMarkedUris( KUriArrayGranularity )
+    {    
+    }
+    
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::NewL
+// ---------------------------------------------------------------------------
+//
+CVPbkContactLinkArray::CStoreUris* CVPbkContactLinkArray::CStoreUris::NewL()
+    {
+    CStoreUris* self = new( ELeave ) CStoreUris;
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::~CStoreUris
+// ---------------------------------------------------------------------------
+//
+CVPbkContactLinkArray::CStoreUris::~CStoreUris()
+    {
+    iMarkedUris.Close();
+    iStores.Close();
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::AppendIfNotFoundL
+// ---------------------------------------------------------------------------
+//    
+void CVPbkContactLinkArray::CStoreUris::AppendIfNotFoundL( 
+        const MVPbkContactStore& aStore )
+    {        
+    if ( iStores.Find( &aStore ) == KErrNotFound )
+        {
+        AppendStoreL( &aStore );
+        }
+    }
+    
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::AppendIfNotFoundL
+// ---------------------------------------------------------------------------
+//    
+void CVPbkContactLinkArray::CStoreUris::AppendIfNotFoundL( 
+        const MVPbkContactLink& aContactLink )
+    {
+    AppendIfNotFoundL( aContactLink.ContactStore() );
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::AppendEmptyL
+// ---------------------------------------------------------------------------
+//    
+void CVPbkContactLinkArray::CStoreUris::AppendEmptyL()
+    {
+    AppendStoreL( NULL );
+    }
+    
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::Count
+// ---------------------------------------------------------------------------
+//    
+inline TInt CVPbkContactLinkArray::CStoreUris::Count() const
+    {
+    return iStores.Count();
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::At
+// ---------------------------------------------------------------------------
+//    
+inline TPtrC CVPbkContactLinkArray::CStoreUris::At( TInt aIndex ) const
+    {
+    const MVPbkContactStore* store = iStores[aIndex];
+    if ( store )
+        {
+        return store->StoreProperties().Uri().UriDes();
+        }
+    else
+        {
+        return TPtrC( KNullDesC );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::FindL
+// ---------------------------------------------------------------------------
+//        
+TInt CVPbkContactLinkArray::CStoreUris::Find( const TDesC& aUri ) const
+    {
+    const TInt count = iStores.Count();
+    for ( TInt i = 0; i < count; ++i )
+        {
+        if ( iStores[i] && iStores[i]->StoreProperties().Uri().Compare( aUri, 
+             TVPbkContactStoreUriPtr::EContactStoreUriAllComponents ) == 0 )
+            {
+            return i;
+            }
+        }
+    return KErrNotFound;
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::Find
+// ---------------------------------------------------------------------------
+//    
+TInt CVPbkContactLinkArray::CStoreUris::Find( 
+        const MVPbkContactStore& aStore )
+    {
+    return iStores.Find( &aStore );
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::ResetAndDestroy
+// ---------------------------------------------------------------------------
+//    
+void CVPbkContactLinkArray::CStoreUris::ResetAndDestroy()
+    {
+    iStores.Reset();
+    iMarkedUris.Reset();
+    }
+    
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::IsUriMarked
+// ---------------------------------------------------------------------------
+//    
+TBool CVPbkContactLinkArray::CStoreUris::IsUriMarked( 
+        const MVPbkContactStore& aStore )
+    {
+    __ASSERT_DEBUG( iMarkedUris.Count() == iStores.Count(),
+        Panic( EPreCond_IsUriMarked ) );
+    
+    TInt pos = iStores.Find( &aStore );
+    if ( pos != KErrNotFound &&
+         iMarkedUris[pos] == KUriMarked )
+        {
+        return ETrue;
+        }
+    return EFalse;
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::MarkUri
+// ---------------------------------------------------------------------------
+//    
+void CVPbkContactLinkArray::CStoreUris::MarkUri( 
+        const MVPbkContactStore& aStore )
+    {
+    __ASSERT_DEBUG( iMarkedUris.Count() == iStores.Count(),
+        Panic( EPreCond_MarkUri ) );
+    
+    TInt pos = iStores.Find( &aStore );
+    if ( pos != KErrNotFound )
+        {
+        iMarkedUris[pos] = KUriMarked;
+        }
+    }
+    
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::UnmarkAllUris
+// ---------------------------------------------------------------------------
+//    
+void CVPbkContactLinkArray::CStoreUris::UnmarkAllUris()
+    {
+    const TInt count = iMarkedUris.Count();
+    for ( TInt i = 0; i < count; ++i )
+        {
+        iMarkedUris[i] = KUriNotMarked;
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::RemoveUnmarkedUris
+// ---------------------------------------------------------------------------
+//    
+void CVPbkContactLinkArray::CStoreUris::RemoveUnmarkedUris()
+    {
+    __ASSERT_DEBUG( iMarkedUris.Count() == iStores.Count(),
+        Panic( EPreCond_RemoveUnmarkedUris ) );
+        
+    const TInt count = iMarkedUris.Count();
+    for ( TInt i = count - 1; i >= 0; --i )
+        {
+        if ( iMarkedUris[i] == KUriNotMarked )
+            {
+            iMarkedUris.Remove( i );
+            iStores.Remove( i );
+            }
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CStoreUris::AppendStoreL
+// ---------------------------------------------------------------------------
+//    
+void CVPbkContactLinkArray::CStoreUris::AppendStoreL( 
+        const MVPbkContactStore* aStore )
+    {
+    __ASSERT_DEBUG( iMarkedUris.Count() == iStores.Count(),
+        Panic( EPreCond_AppendStoreL ) );
+    User::LeaveIfError( iMarkedUris.Append( KUriNotMarked ) );
+    TRAPD( res, iStores.AppendL( aStore ) );
+    if ( res != KErrNone )
+        {
+        // If second AppendL leaves then return to the original state
+        // by deleting the item appended in first AppendL
+        iMarkedUris.Remove( iMarkedUris.Count() - 1 );
+        User::Leave( res );
+        }
+    }
+
+// ======== MEMBER FUNCTIONS ========
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::CVPbkContactLinkArray
+// ---------------------------------------------------------------------------
+//
+inline CVPbkContactLinkArray::CVPbkContactLinkArray()
+    {
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::ConstructL
+// ---------------------------------------------------------------------------
+//
+inline void CVPbkContactLinkArray::ConstructL()
+    {
+    iStoreUris = CStoreUris::NewL();
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::ConstructL
+// ---------------------------------------------------------------------------
+//
+inline void CVPbkContactLinkArray::ConstructL(
+        const TDesC8& aPackedLinks, 
+        const MVPbkContactStoreList& aStoreList)
+    {
+    iStoreUris = CStoreUris::NewL();
+    // unencrypt the packed links descriptor to this array
+    RDesReadStream readStream(aPackedLinks);
+    readStream.PushL();
+    InternalizeFromBufferL(readStream, aStoreList);
+    CleanupStack::PopAndDestroy(&readStream);
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::ConstructL
+// ---------------------------------------------------------------------------
+//
+inline void CVPbkContactLinkArray::ConstructL(
+        RReadStream& aStream, 
+        const MVPbkContactStoreList& aStoreList)
+    {
+    iStoreUris = CStoreUris::NewL();
+    InternalizeFromBufferL( aStream, aStoreList );
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::NewL
+// ---------------------------------------------------------------------------
+//
+EXPORT_C CVPbkContactLinkArray* CVPbkContactLinkArray::NewL()
+    {
+    CVPbkContactLinkArray* self = CVPbkContactLinkArray::NewLC();
+    CleanupStack::Pop();
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::NewLC
+// ---------------------------------------------------------------------------
+//
+EXPORT_C CVPbkContactLinkArray* CVPbkContactLinkArray::NewLC()
+    {
+    CVPbkContactLinkArray* self = new(ELeave) CVPbkContactLinkArray();
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::NewLC
+// ---------------------------------------------------------------------------
+//
+EXPORT_C CVPbkContactLinkArray* CVPbkContactLinkArray::NewLC(
+        const TDesC8& aPackedLinks, 
+        const MVPbkContactStoreList& aStoreList)
+    {
+    CVPbkContactLinkArray* self = new(ELeave) CVPbkContactLinkArray();
+    CleanupStack::PushL(self);
+    self->ConstructL(aPackedLinks, aStoreList);
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::NewLC
+// ---------------------------------------------------------------------------
+//
+EXPORT_C CVPbkContactLinkArray* CVPbkContactLinkArray::NewLC(
+        RReadStream& aStream,
+        const MVPbkContactStoreList& aStoreList)
+    {
+    CVPbkContactLinkArray* self = new(ELeave) CVPbkContactLinkArray();
+    CleanupStack::PushL(self);
+    self->ConstructL(aStream, aStoreList);
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::NewLC
+// ---------------------------------------------------------------------------
+//
+CVPbkContactLinkArray* CVPbkContactLinkArray::NewLC(
+        RReadStream& aStream,
+        MVPbkContactStoreLoader& aLoader )
+    {
+    CVPbkContactLinkArray* self = new( ELeave ) CVPbkContactLinkArray;
+    CleanupStack::PushL( self );
+    self->iStoreLoader = &aLoader;
+    self->ConstructL( aStream, aLoader.ContactStoreListL() );
+    return self;
+    }
+    
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::~CVPbkContactLinkArray
+// ---------------------------------------------------------------------------
+//
+CVPbkContactLinkArray::~CVPbkContactLinkArray()
+    {
+    iLinks.ResetAndDestroy();
+    delete iStoreUris;
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::Count
+// ---------------------------------------------------------------------------
+//
+TInt CVPbkContactLinkArray::Count() const
+    {
+    return iLinks.Count();
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::At
+// ---------------------------------------------------------------------------
+//
+const MVPbkContactLink& CVPbkContactLinkArray::At(TInt aIndex) const
+    {
+    return *iLinks[aIndex];
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::Find
+// ---------------------------------------------------------------------------
+//
+TInt CVPbkContactLinkArray::Find(const MVPbkContactLink& aLink) const
+    {
+    TInt result = KErrNotFound;
+    
+    const TInt count = Count();
+    for (TInt i = 0; i < count; ++i)
+        {
+        if (aLink.IsSame(At(i)))
+            {
+            result = i;
+            break;
+            }
+        }
+    return result;
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::PackLC
+//
+// Linkarray formats:
+//
+// Link array pack format 1:
+// stream := VersionNumber ContactLinkCount ContactLink*
+// VersionNumber := TUint8
+// ContactLinkCount := TUint32
+// ContactLink := UriLength UriDescriptor InternalPackedSize PackInternals
+// UriLength := TInt16
+// UriDescriptor := TChar*
+// InternalPackedSize := TUint32
+// PackInternals := TChar*
+//
+// Link array pack format 2:
+// stream := VersionNumber StoreUriCount StoreUri* ContactLinkCount ContactLink*
+// VersionNumber := TUint8
+// StoreUriCount := TUint16
+// StoreUri := UriLength UriDescriptor
+// UriLength := TUint16
+// UriDescriptor := TChar*
+// ContactLinkCount := TUint32
+// ContactLink := UriIndex InternalPackedSize PackInternals
+// UriIndex := TInt16
+// InternalPackedSize := TUint32
+// PackInternals := TChar*
+// ---------------------------------------------------------------------------
+HBufC8* CVPbkContactLinkArray::PackLC() const
+    {
+    const TInt bufferSize = DoCalculatePackedBufferSizeV2();
+    HBufC8* packagedLinkArray = HBufC8::NewLC( bufferSize );
+    TPtr8 bufPtr( packagedLinkArray->Des() );
+    RDesWriteStream writeStream( bufPtr );
+    writeStream.PushL();
+    DoFillPackedBufferV2L( writeStream );
+    writeStream.CommitL();
+    CleanupStack::PopAndDestroy( &writeStream );
+    
+    return packagedLinkArray;
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::Streamable
+// ---------------------------------------------------------------------------
+//
+const MVPbkStreamable* CVPbkContactLinkArray::Streamable() const
+    {
+    // persistent streaming is not supported
+    return NULL;
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::AppendL
+// ---------------------------------------------------------------------------
+//
+EXPORT_C void CVPbkContactLinkArray::AppendL( MVPbkContactLink* aLink )
+    {
+    if ( aLink )
+        {
+        iStoreUris->AppendIfNotFoundL( *aLink );
+        iLinks.AppendL( aLink );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::InsertL
+// ---------------------------------------------------------------------------
+//
+EXPORT_C void CVPbkContactLinkArray::InsertL( MVPbkContactLink* aLink, 
+        TInt aIndex )
+    {
+    if ( aLink )
+        {
+        iStoreUris->AppendIfNotFoundL( *aLink );
+        iLinks.InsertL( aLink, aIndex );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::Remove
+// ---------------------------------------------------------------------------
+//
+EXPORT_C void CVPbkContactLinkArray::Remove( TInt aIndex )
+    {
+    iLinks.Remove(aIndex);
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::Delete
+// ---------------------------------------------------------------------------
+//    
+EXPORT_C void CVPbkContactLinkArray::Delete( TInt aIndex )
+    {
+    MVPbkContactLink* link = iLinks[aIndex];
+    Remove( aIndex );
+    delete link;
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::ResetAndDestroy
+// ---------------------------------------------------------------------------
+//    
+EXPORT_C void CVPbkContactLinkArray::ResetAndDestroy()
+    {
+    iLinks.ResetAndDestroy();
+    iStoreUris->ResetAndDestroy();
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::Reset
+// ---------------------------------------------------------------------------
+//        
+EXPORT_C void CVPbkContactLinkArray::Reset()
+    {
+    iLinks.Reset();
+    iStoreUris->ResetAndDestroy();
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::PackedBufferSize
+// ---------------------------------------------------------------------------
+//        
+EXPORT_C TInt CVPbkContactLinkArray::PackedBufferSize() const
+    {
+    return DoCalculatePackedBufferSizeV2(); // V2
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::DoCalculatePackedBufferSizeV2
+// NOTE: This function also removes URIs that are not refered from links
+// ---------------------------------------------------------------------------
+//
+TInt CVPbkContactLinkArray::DoCalculatePackedBufferSizeV2() const
+    {
+    // Unmark all URIs
+    iStoreUris->UnmarkAllUris();
+    
+    TInt bufferSize = KLinkArrayExternalizeSizes::KVersionNumberSize +
+        KLinkArrayExternalizeSizes::KStoreUriCountSize + 
+        KLinkArrayExternalizeSizes::KContactLinkCountSize;
+    
+    const TInt linkCount = iLinks.Count();
+    for (TInt i = 0; i < linkCount; ++i)
+        {
+        const MVPbkContactLink& link = *iLinks[i];
+        const MVPbkContactStore& store = link.ContactStore();
+        // Add URI length if not added before
+        if ( !iStoreUris->IsUriMarked( store ) )
+            {
+            bufferSize += KLinkArrayExternalizeSizes::KUriLengthSize;
+            bufferSize += store.StoreProperties().Uri().UriDes().Size();
+            // Mark URI so that its length is not added again
+            iStoreUris->MarkUri( store );
+            }
+        // Add the link internals size
+        bufferSize += KLinkArrayExternalizeSizes::KUriIndexSize;
+        bufferSize += KLinkArrayExternalizeSizes::KInternalPackedSize;
+        bufferSize += link.Packing().InternalPackedSize();
+        }
+
+    // Remove URIs that are not used
+    iStoreUris->RemoveUnmarkedUris();
+    // Reset the state of iStoreUris marked URIs
+    iStoreUris->UnmarkAllUris();
+    
+    return bufferSize;
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::DoFillPackedBufferV2L
+// ---------------------------------------------------------------------------
+//
+void CVPbkContactLinkArray::DoFillPackedBufferV2L(
+        RWriteStream& aWriteStream) const
+    {
+    // write version number
+    aWriteStream.WriteUint8L(
+        KLinkArrayStatic::KVPbkPackagedLinkArrayVersionV2);
+    // write store URI count
+    const TInt uriCount = iStoreUris->Count();
+    aWriteStream.WriteUint16L( uriCount );
+    // write store URIs
+    for ( TInt i = 0; i < uriCount; ++i )
+        {
+        TPtrC uriPtr( iStoreUris->At( i ) );
+        const TInt uriLength = uriPtr.Length();
+        aWriteStream.WriteUint16L( uriLength );
+        aWriteStream.WriteL( uriPtr, uriLength );
+        }
+    const TInt linkCount = iLinks.Count();
+    // write the contact link count to the stream
+    aWriteStream.WriteUint32L( linkCount );
+    // write links
+    for (TInt i = 0; i < linkCount; ++i)
+        {
+        const MVPbkContactLink& link = *iLinks[i];
+        const TInt uriIndex = iStoreUris->Find( link.ContactStore() );
+        // write URI index
+        aWriteStream.WriteInt16L( uriIndex );
+        // write URI internal size
+        aWriteStream.WriteUint32L( link.Packing().InternalPackedSize() );
+        // write internals
+        link.Packing().PackInternalsL(aWriteStream);
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::InternalizeFromBufferL
+// ---------------------------------------------------------------------------
+//
+void CVPbkContactLinkArray::InternalizeFromBufferL( 
+		RReadStream& aReadStream, 
+        const MVPbkContactStoreList& aStoreList )
+    {
+    // check correct version number of link array
+    const TInt versionNumber = aReadStream.ReadUint8L();
+    if ( versionNumber == KLinkArrayStatic::KVPbkPackagedLinkArrayVersionV1 )
+        {
+        DoInternalizeFromBufferL( aReadStream, aStoreList );
+        }
+    else if ( versionNumber == 
+              KLinkArrayStatic::KVPbkPackagedLinkArrayVersionV2 )
+        {
+        DoInternalizeFromBufferV2L( aReadStream, aStoreList );
+        }
+    else
+        {
+        // Invalid link array version
+        User::Leave( KErrArgument );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::DoInternalizeFromBufferL
+// Version 1
+// ---------------------------------------------------------------------------
+//
+void CVPbkContactLinkArray::DoInternalizeFromBufferL(
+        RReadStream& aReadStream, 
+        const MVPbkContactStoreList& aStoreList)
+    {
+    // read contact link count from the stream
+    const TInt contactLinkCount = aReadStream.ReadUint32L();
+
+    // read each contact link from the stream
+    for (TInt i = 0; i < contactLinkCount; ++i)
+        {
+        // read the store URI
+        const TInt uriLength = aReadStream.ReadUint16L();
+        HBufC* uriBuffer = HBufC::NewLC(uriLength);
+        TPtr uriPtr = uriBuffer->Des();
+        aReadStream.ReadL(uriPtr, uriLength);
+        // find the store corresponding to the URI
+        MVPbkContactStore* store =
+            aStoreList.Find(TVPbkContactStoreUriPtr(uriPtr));
+        CleanupStack::PopAndDestroy(uriBuffer);
+
+        const TInt internalPackedSize = aReadStream.ReadUint32L();
+        if(store)
+            {
+            // create link and add it to this array
+            MVPbkContactLink* link = 
+                store->CreateLinkFromInternalsLC(aReadStream);
+            AppendL(link);
+            CleanupStack::Pop(); // link
+            }
+        else
+            {
+            // skip PackInternals that are unknown
+            aReadStream.ReadL(internalPackedSize);
+            }
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CVPbkContactLinkArray::DoInternalizeFromBufferV2L
+// Version 2
+// ---------------------------------------------------------------------------
+//
+void CVPbkContactLinkArray::DoInternalizeFromBufferV2L(
+        RReadStream& aReadStream, 
+        const MVPbkContactStoreList& aStoreList)
+    {
+    // Read store URI count
+    TInt uriCount = aReadStream.ReadUint16L();
+    // Read URIs
+    for ( TInt i = 0; i < uriCount; ++i )
+        {
+        const TInt uriLength = aReadStream.ReadUint16L();
+        HBufC* uriBuffer = HBufC::NewLC(uriLength);
+        TPtr uriPtr = uriBuffer->Des();
+        aReadStream.ReadL( uriPtr, uriLength );
+        MVPbkContactStore* store = aStoreList.Find( uriPtr );
+        if ( !store && iStoreLoader )
+            {
+            // Try to load the store
+            iStoreLoader->LoadContactStoreL( uriPtr );
+            store = aStoreList.Find( uriPtr );
+            }
+        
+        if ( store )
+            {
+            iStoreUris->AppendIfNotFoundL( *store );
+            }
+        else
+            {
+            // Append empty URI to keep ÜRI indexes valid
+            iStoreUris->AppendEmptyL();
+            }
+        CleanupStack::PopAndDestroy( uriBuffer );
+        }
+    
+    if ( uriCount != iStoreUris->Count() )
+        {
+        // There must be as many items in the iStoreUris as uriCount
+        // because otherwise URI indexes are not valid. We cannot
+        // continue reading link data in that case.
+        User::Leave( KErrCorrupt );
+        }
+    
+    // read contact link count from the stream
+    const TInt contactLinkCount = aReadStream.ReadUint32L();
+    // read each contact link from the stream
+    for (TInt i = 0; i < contactLinkCount; ++i)
+        {
+        // read the store URI index
+        const TInt uriIndex = aReadStream.ReadInt16L();
+        // read the size of the internals
+        const TInt internalPackedSize = aReadStream.ReadUint32L();
+        if ( uriIndex >= 0 && uriIndex < uriCount )
+            {
+            const TDesC& uri = iStoreUris->At( uriIndex );
+            // find the store corresponding to the URI
+            MVPbkContactStore* store = aStoreList.Find( uri );
+            if( store )
+                {
+                // create link and add it to this array
+                MVPbkContactLink* link = 
+                    store->CreateLinkFromInternalsLC( aReadStream );
+                AppendL( link );
+                CleanupStack::Pop(); // link
+                }
+            else
+                {
+                // skip PackInternals that are unknown
+                aReadStream.ReadL( internalPackedSize );
+                }
+            }
+        else
+            {
+            // skip PackInternals that are unknown
+            aReadStream.ReadL( internalPackedSize );
+            }
+        }
+    }
+// end of file