omads/omadsextensions/adapters/contactsgroup/src/contactsgrpdatastore.cpp
branchRCL_3
changeset 24 8e7494275d3a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/omads/omadsextensions/adapters/contactsgroup/src/contactsgrpdatastore.cpp	Tue Aug 31 15:05:37 2010 +0300
@@ -0,0 +1,1268 @@
+/*
+* Copyright (c) 2009-2010 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:  Part of SyncML Data Synchronization Plug In Adapter
+*
+*/
+
+
+#include <sysutil.h> 
+#include <cntfldst.h>
+
+#include "contactsgrpdatastore.h"
+#include "contactsgrpconverter.h"
+#include "changefinder.h"
+#include "contactsgrpdataproviderdefs.h"
+#include "logger.h"
+
+const TInt KDefaultBufferSize = 1024;
+const TInt KDataBufferNotReady = -1;
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::CContactsGrpDataStore
+// C++ default constructor can NOT contain any code, that might leave.
+// -----------------------------------------------------------------------------
+CContactsGrpDataStore::CContactsGrpDataStore() :
+    iKey( TKeyArrayFix( _FOFF( TNSmlSnapshotItem, ItemId() ), ECmpTInt ) )
+    {
+    TRACE_FUNC;
+    }
+    
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::ConstructL
+// Symbian 2nd phase constructor, can leave.
+// -----------------------------------------------------------------------------    
+void CContactsGrpDataStore::ConstructL()
+    {
+    TRACE_FUNC_ENTRY;
+    
+    User::LeaveIfError( iFs.Connect() );
+    
+    iNewItems       = new ( ELeave ) CNSmlDataItemUidSet;
+    iDeletedItems   = new ( ELeave ) CNSmlDataItemUidSet;
+    iUpdatedItems   = new ( ELeave ) CNSmlDataItemUidSet;
+    iEmptyList      = new ( ELeave ) CNSmlDataItemUidSet;
+    
+    iConverter = CContactsGrpConverter::NewL();
+    
+    TRACE_FUNC_EXIT;
+    }
+    
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::~CContactsGrpDataStore
+// Destructor
+// -----------------------------------------------------------------------------    
+CContactsGrpDataStore::~CContactsGrpDataStore()
+    {
+    TRACE_FUNC_ENTRY;
+
+    delete iDataBuffer;
+    delete iNewItems;
+    delete iDeletedItems;
+    delete iUpdatedItems;
+    delete iEmptyList;
+    
+    if ( iChangeFinder )
+        {
+        TRAPD( error, iChangeFinder->CloseL() );
+        if ( error != KErrNone )
+            {
+            LOGGER_WRITE_1( "iChangeFinder->CloseL() leaved with %d", error );
+            }
+        }
+    delete iChangeFinder;
+    
+    delete iConverter;
+    
+    delete iContactsDb;
+    
+    iFs.Close();
+    TRACE_FUNC_EXIT;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+CContactsGrpDataStore* CContactsGrpDataStore::NewL()
+    {
+    TRACE_FUNC_ENTRY;
+    CContactsGrpDataStore* self = CContactsGrpDataStore::NewLC();
+    CleanupStack::Pop( self );
+    TRACE_FUNC_EXIT;
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::NewLC
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+CContactsGrpDataStore* CContactsGrpDataStore::NewLC()
+    {
+    TRACE_FUNC_ENTRY;
+    CContactsGrpDataStore* self = new ( ELeave ) CContactsGrpDataStore();
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    TRACE_FUNC_EXIT;
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoOpenL
+// Opens database.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoOpenL( const TDesC& /*aStoreName*/,
+    MSmlSyncRelationship& aContext, TRequestStatus& aStatus )
+    {
+    TRACE_FUNC_ENTRY;
+    
+    iCallerStatus = &aStatus;           
+    *iCallerStatus = KRequestPending;
+    
+    if ( iContactsDb )
+        {
+        // already opened
+        User::RequestComplete( iCallerStatus, KErrInUse );
+        LOGGER_WRITE( "CContactsGrpDataStore::DoOpenL failed with KErrInUse." );
+        return;
+        }
+    
+    // Create ChangeFinder
+    if ( iChangeFinder )
+        {
+        delete iChangeFinder;
+        iChangeFinder = NULL;
+        }
+    iChangeFinder = CChangeFinder::NewL( aContext, iKey, iHasHistory );
+    
+    iContactsDb = CContactDatabase::OpenL();
+    
+    RegisterSnapshotL();
+    
+    User::RequestComplete( iCallerStatus, KErrNone );
+    TRACE_FUNC_EXIT;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCancelRequest
+// Not supported, does nothing.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCancelRequest()
+    {
+    TRACE_FUNC;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoStoreName
+// Returns the name of the DataStore
+// -----------------------------------------------------------------------------
+const TDesC& CContactsGrpDataStore::DoStoreName() const
+    {
+    TRACE_FUNC;
+    
+    if ( iContactsDb )
+        {
+        return KContactsGrpStoreName;
+        }
+    else
+        {
+        return KNullDesC;
+        }
+    }
+    
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoBeginTransactionL
+// Transactions are not supported.
+// -----------------------------------------------------------------------------    
+void CContactsGrpDataStore::DoBeginTransactionL()
+    {
+    TRACE_FUNC;
+    User::Leave( KErrNotSupported );
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCommitTransactionL
+// Transactions are not supported.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCommitTransactionL( TRequestStatus& aStatus )
+    {
+    TRACE_FUNC;
+    iCallerStatus = &aStatus;
+    User::RequestComplete( iCallerStatus, KErrNotSupported );
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoRevertTransaction
+// Transactions are not supported.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoRevertTransaction( TRequestStatus& aStatus )
+    {
+    TRACE_FUNC;
+    iCallerStatus = &aStatus;
+    User::RequestComplete( iCallerStatus, KErrNotSupported );
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoBeginBatchL
+// Batching is not supported.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoBeginBatchL()
+    {
+    TRACE_FUNC;
+    User::Leave( KErrNotSupported );
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCommitBatchL
+// Batching is not supported
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCommitBatchL( RArray<TInt>& /*aResultArray*/, TRequestStatus& aStatus )
+    {
+    TRACE_FUNC;
+    iCallerStatus = &aStatus;
+    User::RequestComplete( iCallerStatus, KErrNotSupported );
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCancelBatch
+// Batching is not supported
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCancelBatch()
+    {
+    TRACE_FUNC;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoSetRemoteStoreFormatL
+// Not supported
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoSetRemoteStoreFormatL( const CSmlDataStoreFormat& /*aServerDataStoreFormat*/ )
+    {
+    TRACE_FUNC;
+    }
+    
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoSetRemoteMaxObjectSize
+// Not supported
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoSetRemoteMaxObjectSize( TInt /*aServerMaxObjectSize*/ )
+    {
+    TRACE_FUNC;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoMaxObjectSize
+// Reads the maximum object size from the central repository
+// -----------------------------------------------------------------------------
+TInt CContactsGrpDataStore::DoMaxObjectSize() const
+    {
+    TRACE_FUNC;
+    return 0; // no limit
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoOpenItemL
+// Opens item in the DataStore, reads it (either completely or partially) 
+// to the temporary buffer where it can be later read to the remote database.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoOpenItemL(
+    TSmlDbItemUid aUid, 
+    TBool& aFieldChange, 
+    TInt& aSize, 
+    TSmlDbItemUid& aParent, 
+    TDes8& aMimeType, 
+    TDes8& aMimeVer, 
+    TRequestStatus& aStatus )
+    {
+    TRACE_FUNC_ENTRY;
+    LOGGER_WRITE_1( "aUid: %d", aUid );
+    
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    aFieldChange = EFalse;
+    aParent = KErrNotFound;
+    
+    delete iDataBuffer;
+    iDataBuffer = NULL;
+    
+    if ( !iContactsDb )
+        {
+        User::Leave( KErrNotReady );
+        }
+    
+    iDataBuffer = CBufFlat::NewL( KDefaultBufferSize ); 
+    iReaderPosition = 0;
+    
+    CContactItem* item = iContactsDb->ReadContactLC( aUid );
+    if ( item->Type() != KUidContactGroup )
+        {
+        CleanupStack::PopAndDestroy( item );
+        User::Leave( KErrNotFound );
+        }
+
+    iConverter->ImportDbItemL( *static_cast<CContactGroup*>(item) );
+    
+    CleanupStack::PopAndDestroy( item );
+    
+    iConverter->ExportVCardL( *iDataBuffer );
+    iConverter->ResetL();
+    aSize = iDataBuffer->Size();
+    
+    // Set mime type to correct one
+    AssignString( aMimeType, KContactsGrpItemMimeType );
+    AssignString( aMimeVer, KContactsGrpItemMimeVersion );
+    
+    User::RequestComplete( iCallerStatus, KErrNone );   
+    iCurrentState = EContactGrpOpening;
+    
+    TRACE_FUNC_EXIT;   
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCreateItemL
+// Create new item to the message store.
+// Return the id number of the newly created item
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCreateItemL(
+    TSmlDbItemUid& aUid, 
+    TInt aSize, 
+    TSmlDbItemUid /*aParent*/, 
+    const TDesC8& /*aMimeType*/, 
+    const TDesC8& /*aMimeVer*/, 
+    TRequestStatus& aStatus )
+    {
+    TRACE_FUNC_ENTRY;
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    // Ensure that we're in a proper state
+    if ( iCurrentState != EOpenAndWaiting )
+        {
+        LOGGER_WRITE_1( "Warning: Unexpected current state: %d", iCurrentState );
+        }
+        
+    if ( !iContactsDb )
+        {
+        LOGGER_WRITE( "iContactsDb not ready" );
+        User::Leave( KErrNotReady );
+        }
+    
+    // Ensure that we've got enough disk space for the item
+    if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, aSize, EDriveC ) )
+        {
+        User::RequestComplete( iCallerStatus, KErrDiskFull );
+        LOGGER_WRITE( "SysUtil::DiskSpaceBelowCriticalLevelL failed with KErrDiskFull." );
+        return;
+        }
+    
+    // item uid is updated when groups is saved to db.
+    iCurrentItem = &aUid; 
+    // Save current operation-state so we know what operation
+    // is needed to do on DoCommitItemL
+    iCurrentState = EContactGrpCreating;
+                
+    delete iDataBuffer;
+    iDataBuffer = NULL;
+    iDataBuffer = CBufFlat::NewL( KDefaultBufferSize );
+    iWriterPosition = 0;
+    
+    User::RequestComplete( iCallerStatus, KErrNone );  
+    TRACE_FUNC_EXIT;    
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoReplaceItemL
+// Begin the replace operation, ensure that the item really exists
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoReplaceItemL(
+    TSmlDbItemUid aUid, 
+    TInt aSize, 
+    TSmlDbItemUid /*aParent*/,
+    TBool /*aFieldChange*/, 
+    TRequestStatus& aStatus )
+    {
+    TRACE_FUNC_ENTRY;
+    LOGGER_WRITE_1("aUid: %d", aUid);
+    
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    if ( !iContactsDb )
+        {
+        LOGGER_WRITE( "iContactsDb not ready" );
+        User::Leave( KErrNotReady );
+        }
+        
+    // Ensure that we've got enough disk space for the item
+    if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, aSize, EDriveC ) )
+        {
+        User::RequestComplete( iCallerStatus, KErrDiskFull );
+        LOGGER_WRITE( "SysUtil::DiskSpaceBelowCriticalLevelL failed with KErrDiskFull." );
+        return;
+        }
+    
+    // check that item exist and is Group-type
+    CContactItem* item = iContactsDb->ReadContactLC( aUid );
+    if ( item->Type() != KUidContactGroup )
+        {
+        CleanupStack::PopAndDestroy( item );
+        User::Leave( KErrNotFound );
+        }
+    CleanupStack::PopAndDestroy( item );
+    
+    // init databuffer
+    delete iDataBuffer;
+    iDataBuffer = NULL;
+    iDataBuffer = CBufFlat::NewL( KDefaultBufferSize );
+    iWriterPosition = 0;
+    
+    // Save current item, so we know what item needs to be replaced
+    iItemToBeReplaced = aUid;
+    // Save current operation-state so we know what operation
+    // is needed to do on DoCommitItemL
+    iCurrentState = EContactGrpUpdating;
+    
+    User::RequestComplete( iCallerStatus, KErrNone );   
+    
+    TRACE_FUNC_EXIT;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoReadItemL
+// Read specified amount of data from the temporary buffer
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoReadItemL( TDes8& aBuffer )
+    {
+    TRACE_FUNC_ENTRY;
+    
+    if ( iCurrentState != EContactGrpOpening || !iDataBuffer )
+        {
+        LOGGER_WRITE_1( "Unexpected state %d", iCurrentState );
+        User::Leave( KErrNotReady );
+        }
+        
+    if ( iReaderPosition == KDataBufferNotReady )
+        {
+        LOGGER_WRITE( "No data to read" );
+        User::Leave( KErrEof );
+        }
+    
+    // Thiw is how much data there is left in the buffer    
+    TInt left = iDataBuffer->Size() - iReaderPosition;
+    
+    if ( left > 0 )
+        {
+        // This is how much there's space in the destination buffer
+        TInt destSize = aBuffer.MaxSize();
+        
+        // This is how much we can read
+        TInt toRead = destSize < left ? destSize : left;
+        
+        // Read the data from the buffer, then update the position      
+        iDataBuffer->Read( iReaderPosition, aBuffer, toRead );
+        iReaderPosition += toRead;
+        }
+    else
+        {
+        iReaderPosition = KDataBufferNotReady;
+        LOGGER_WRITE( "No data to read" );
+        User::Leave( KErrEof );
+        }   
+        
+    TRACE_FUNC_EXIT;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoWriteItemL
+// Write specified amount of data to the temporary buffer
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoWriteItemL( const TDesC8& aData )
+    {
+    TRACE_FUNC_ENTRY;
+    
+    if ( iCurrentState != EContactGrpCreating && iCurrentState != EContactGrpUpdating )
+        {
+        LOGGER_WRITE_1( "Unexpected current state: %d", iCurrentState );
+        User::Leave( KErrNotReady );
+        }
+    
+    // Calculate total size
+    TInt totalSize = aData.Size() + iDataBuffer->Size();
+
+    // Ensure that we've got enough disk space for the item
+    if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, totalSize, EDriveC ) )
+        {
+        User::RequestComplete( iCallerStatus, KErrDiskFull );
+        LOGGER_WRITE("SysUtil::DiskSpaceBelowCriticalLevelL failed with KErrDiskFull.");
+        return;
+        }
+        
+    // Add data to buffer       
+    iDataBuffer->InsertL( iWriterPosition, aData );
+    iWriterPosition += aData.Size();
+    
+    TRACE_FUNC_EXIT;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCommitItemL
+// Commits item from temporary buffer to the message store
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCommitItemL( TRequestStatus& aStatus )
+    {
+    TRACE_FUNC_ENTRY;
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    // Check that we're in proper state
+    if ( iCurrentState != EContactGrpCreating && iCurrentState != EContactGrpUpdating )
+        {
+        User::RequestComplete( iCallerStatus, KErrNotReady );
+        LOGGER_WRITE_1( "Unexpected current state: %d", iCurrentState );
+        return;
+        }
+        
+    if ( iDataBuffer->Size() <= 0 )
+        {
+        User::RequestComplete( iCallerStatus, KErrNotReady );
+        LOGGER_WRITE_1( "Data buffer has no data (%d)", iDataBuffer->Size() );
+        return; 
+        }
+    
+    if ( !iContactsDb )
+        {
+        LOGGER_WRITE( "iContactsDb not ready" );
+        User::Leave( KErrNotReady );
+        }
+
+    iDataBuffer->Compress();
+    iConverter->ImportVCardL( iDataBuffer->Ptr(0) );
+    
+    const CContactIdArray& contactArr = iConverter->ItemsContained();
+    
+    if ( iCurrentState == EContactGrpCreating )
+        {
+        *iCurrentItem = CreateNewGroupL( iConverter->GroupLabel(), contactArr );
+        }
+    else
+        {
+        ReplaceGroupL( iItemToBeReplaced, iConverter->GroupLabel(), contactArr);
+        }
+    iConverter->ResetL();
+    
+    // Destroy buffer
+    delete iDataBuffer;
+    iDataBuffer = NULL;
+    iWriterPosition = 0;
+    
+    // Restore state and signal we're done
+    iCurrentState = EOpenAndWaiting;
+    User::RequestComplete( iCallerStatus, KErrNone );
+    
+    TRACE_FUNC_EXIT;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::CreateNewGroupL
+// Creates new groupd to db.
+// -----------------------------------------------------------------------------
+TContactItemId CContactsGrpDataStore::CreateNewGroupL( const TDesC& aLabel,
+        const CContactIdArray& aContactArray )
+    {
+    TRACE_FUNC_ENTRY;
+    LOGGER_WRITE_1("aLabel: %S", &aLabel );
+    
+    // Check for duplicates
+    if ( GroupNameExistsL( aLabel ) )
+        {
+        LOGGER_WRITE("Duplicate group name found, leave KErrAlreadyExists");
+        User::Leave( KErrAlreadyExists );
+        }
+    
+    CContactGroup* group =
+        static_cast<CContactGroup*>
+        (iContactsDb->CreateContactGroupLC( aLabel ));
+    
+    // Create new contact group
+    
+    TContactItemId groupId = group->Id();
+    LOGGER_WRITE_1("new group id: %d", groupId);
+    
+    TBool partiallyUpdated(EFalse);
+    TTime lastMod;
+    lastMod.UniversalTime();
+    TInt err(KErrNone);
+    for ( TInt i=0; i<aContactArray.Count(); i++ )
+        {
+        LOGGER_WRITE_1("Add contact: %d", aContactArray[i] );
+        TRAP( err, iContactsDb->AddContactToGroupL( aContactArray[i], groupId ));
+        if ( err )
+            {
+            LOGGER_WRITE_2("Warning: AddContactToGroupL, contact: %d, err: %d", aContactArray[i], err);
+            partiallyUpdated = ETrue;
+            }
+        else
+            {
+            // If succesfully added, update contact timestamp so contact sync sees those
+            // contacts as changed ones.
+            UpdateContactLastMod( aContactArray[i], lastMod );
+            }
+        }
+    
+    CleanupStack::PopAndDestroy( group );
+    group = NULL;
+    
+    // if item was only partially updated, do not register to snapshot
+    //   --> on next sync item is marked as updated
+    if ( !partiallyUpdated )
+        {
+        // reload group
+        group = static_cast<CContactGroup*>
+            (iContactsDb->ReadContactLC( groupId ));
+        // Inform ChangeFinder of new item
+        TSnapshotItem snapshotItem( groupId );
+        snapshotItem.CreateHashL( *group );
+        iChangeFinder->ItemAddedL( snapshotItem );
+        
+        CleanupStack::PopAndDestroy( group );
+        }
+    
+    
+    
+    TRACE_FUNC_EXIT;
+    return groupId;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::ReplaceGroupL
+// Replaces existing group on db
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::ReplaceGroupL( TContactItemId aGroupId, const TDesC& aLabel,
+            const CContactIdArray& aContactArray )
+    {
+    TRACE_FUNC_ENTRY;
+    LOGGER_WRITE_1("aGroupId: %d", aGroupId);
+    LOGGER_WRITE_1("aLabel: %S", &aLabel);
+    TBool partiallyUpdated(EFalse);
+    // check that item exist and is Group-type
+    // The LX suffix means that the lock record of the contact item is left on the cleanup stack.
+    CContactItem* item = iContactsDb->OpenContactLX( aGroupId );
+    CleanupStack::PushL( item );
+    if ( item->Type() != KUidContactGroup )
+        {
+        LOGGER_WRITE("Type was not KUidContactGroup, leaving KErrNotFound");
+        User::Leave( KErrNotFound );
+        }
+    
+    // cast item to CContactGroup type
+    CContactGroup* groupItem = static_cast<CContactGroup*>(item);
+    TContactItemId groupId = groupItem->Id();
+    
+    TBool labelUpdated(EFalse);
+    // Check is group label changed
+    if ( groupItem->HasItemLabelField() )
+        {
+        TPtrC dbLabel = groupItem->GetGroupLabelL();
+        if ( aLabel.Compare( dbLabel ) != 0 )
+            {
+            // didn't match, update label.
+            // Check that new name does not already exist
+            if ( GroupNameExistsL( aLabel ) )
+                {
+                LOGGER_WRITE("Name already exists, leave KErrAlreadyExists");
+                User::Leave( KErrAlreadyExists );
+                }
+            LOGGER_WRITE("Update label" );
+            groupItem->SetGroupLabelL( aLabel );
+            iContactsDb->CommitContactL( *item );
+            labelUpdated = ETrue;
+            }
+        }
+    TTime lastMod;
+    lastMod.UniversalTime();
+    // Update joined contacts
+    TInt err(KErrNone);
+    // add new items to current group
+    for ( TInt i=0; i<aContactArray.Count(); i++ )
+        {
+        TContactItemId newItem = aContactArray[i];
+        TBool founded = groupItem->ContainsItem( newItem );
+        if ( !founded )
+            {
+            // item does not exist on current group, add it.
+            LOGGER_WRITE_1("Add contact: %d", newItem)
+            TRAP( err, iContactsDb->AddContactToGroupL( newItem, groupId ));
+            if ( err )
+                {
+                LOGGER_WRITE_2("Warning: Could not add contact: %d, err: %d", newItem, err);
+                partiallyUpdated = ETrue;
+                }
+            else
+                {
+                // Update contact timestamp
+                UpdateContactLastMod( newItem, lastMod );
+                }
+            }
+        else
+            {
+            LOGGER_WRITE_1("contact( %d ) already exist on group, no need to update", newItem)
+            }
+        }
+    
+    // remove all missing items from group
+    CContactIdArray* oldIdArr = groupItem->ItemsContainedLC();
+    if ( oldIdArr )
+        {
+        for ( TInt i=0; i<oldIdArr->Count(); i++ )
+            {
+            TContactItemId oldItem = (*oldIdArr)[i];
+            TInt err = aContactArray.Find( oldItem );
+            if ( err == KErrNotFound )
+                {
+                LOGGER_WRITE_1("Remove contact: %d", oldItem );
+                // old item was not found from new item list, remove it.
+                TRAP( err, iContactsDb->RemoveContactFromGroupL(oldItem, groupId));
+                if ( err )
+                    {
+                    LOGGER_WRITE_2("Warning: Could not remove contact: %d, err: ",oldItem, err );
+                    partiallyUpdated = ETrue;
+                    }
+                else
+                    {
+                    UpdateContactLastMod( oldItem, lastMod );
+                    }
+                }
+            // Update all contacts in group if label is changed
+            else if ( labelUpdated )
+                {
+                UpdateContactLastMod( oldItem, lastMod );
+                }
+            }
+        }
+    CleanupStack::PopAndDestroy( oldIdArr );
+    CleanupStack::PopAndDestroy( 2 ); // item, lock object 
+    
+    // if item was only partially updated, do not register to snapshot
+    //   --> on next sync item is marked as updated
+    if ( !partiallyUpdated )
+        {
+        // reload group
+        groupItem = NULL;
+        groupItem = static_cast<CContactGroup*>
+            (iContactsDb->ReadContactLC( groupId ));
+        // Inform ChangeFinder of updated item
+        TSnapshotItem snapshotItem( groupId );
+        snapshotItem.CreateHashL( *groupItem );
+        iChangeFinder->ItemUpdatedL( snapshotItem );
+        
+        CleanupStack::PopAndDestroy( groupItem );
+        }
+    
+    TRACE_FUNC_EXIT;
+    }
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCloseItem
+// Closes open item in the data store
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCloseItem()
+    {
+    TRACE_FUNC_ENTRY;
+    // Reset read buffer 
+    iReaderPosition = KDataBufferNotReady;
+    delete iDataBuffer;
+    iDataBuffer = NULL;
+    
+    // Make sure that we're opened an item
+    if ( iCurrentState != EContactGrpOpening )
+        {
+        LOGGER_WRITE_1( "WARNING: Invalid state %d.", iCurrentState );
+        }
+    // Start to wait for the next operation     
+   iCurrentState = EOpenAndWaiting;
+    
+    TRACE_FUNC_EXIT;   
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoMoveItemL
+// Not supported.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoMoveItemL( TSmlDbItemUid /*aUid*/,
+    TSmlDbItemUid /*aNewParent*/, TRequestStatus& aStatus )
+    {
+    TRACE_FUNC_ENTRY;
+    
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    // Restore state and signal we're done
+    User::RequestComplete( iCallerStatus, KErrNotSupported );
+    
+    TRACE_FUNC_EXIT;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoDeleteItemL
+// Removes item from the message store
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoDeleteItemL( TSmlDbItemUid aUid, TRequestStatus& aStatus )
+    {
+    TRACE_FUNC_ENTRY;
+    LOGGER_WRITE_1( "aUid: %d", aUid );
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    // Check that we're in proper state
+    if ( iCurrentState != EOpenAndWaiting ) 
+        {
+        LOGGER_WRITE_1("CContactsGrpDataStore::DoDeleteItemL, Incorrect state: %d", iCurrentState);
+        }
+    
+    if ( !iContactsDb )
+        {
+        LOGGER_WRITE( "iContactsDb not ready" );
+        User::Leave( KErrNotReady );
+        }
+    
+    // check that item type is ContactGroup
+    CContactItem* item = iContactsDb->ReadContactLC( aUid );
+    if ( item->Type() != KUidContactGroup )
+        {
+        LOGGER_WRITE("Item was not ContactGroup");
+        CleanupStack::PopAndDestroy( item );
+        User::Leave( KErrNotFound );
+        }
+    
+    // Update all contacts in group
+    CContactGroup* groupItem = static_cast<CContactGroup*>(item);
+    CContactIdArray* contactArr = groupItem->ItemsContainedLC();
+    if ( contactArr )
+        {
+        TTime lastMod;
+        lastMod.UniversalTime();
+        for ( TInt i=0; i<contactArr->Count(); i++ )
+            {
+            TContactItemId contactId = (*contactArr)[i];
+            UpdateContactLastMod( contactId, lastMod );
+            }
+        }
+    CleanupStack::PopAndDestroy( contactArr );
+    CleanupStack::PopAndDestroy( item );
+    
+    // delete group
+    iContactsDb->DeleteContactL( aUid );
+    
+    TSnapshotItem snapshotItem( aUid );     
+    iChangeFinder->ItemDeleted( snapshotItem );
+    
+    User::RequestComplete( iCallerStatus, KErrNone );
+        
+    TRACE_FUNC_EXIT;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoSoftDeleteItemL
+// Soft delete isn't supported.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoSoftDeleteItemL(TSmlDbItemUid /*aUid*/, TRequestStatus& aStatus)
+    {
+    TRACE_FUNC_ENTRY;
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    User::RequestComplete(iCallerStatus, KErrNotSupported); 
+    TRACE_FUNC_EXIT;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoDeleteAllItemsL
+// Deletes all items in the standard folders of bookmark store
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoDeleteAllItemsL( TRequestStatus& aStatus )
+    {
+    TRACE_FUNC_ENTRY;
+
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    if ( !iContactsDb )
+        {
+        LOGGER_WRITE( "iContactsDb is not opened, Leaving KErrNotReady");
+        User::Leave( KErrNotReady );
+        }
+    
+    CContactIdArray* groups = iContactsDb->GetGroupIdListL();
+    if ( groups )
+        {
+        CleanupStack::PushL( groups );
+        TTime lastMod;
+        lastMod.UniversalTime();
+        
+        // update all contacts in all groups
+        for ( TInt i=0; i<groups->Count(); i++ )
+            {
+            // check that item type is ContactGroup
+            CContactItem* item = iContactsDb->ReadContactLC( (*groups)[i] );
+            
+            // Update all contacts in group
+            CContactGroup* groupItem = static_cast<CContactGroup*>(item);
+            CContactIdArray* contactArr = groupItem->ItemsContainedLC();
+            if ( contactArr )
+                {
+                for ( TInt i=0; i<contactArr->Count(); i++ )
+                    {
+                    TContactItemId contactId = (*contactArr)[i];
+                    UpdateContactLastMod( contactId, lastMod );
+                    }
+                }
+            CleanupStack::PopAndDestroy( contactArr );
+            CleanupStack::PopAndDestroy( item );
+            }
+        
+        // delete all groups
+        iContactsDb->DeleteContactsL( *groups );
+        CleanupStack::PopAndDestroy( groups );
+        }
+    
+    // Reset the whole change finder
+    iChangeFinder->ResetL();
+    
+    User::RequestComplete( iCallerStatus, KErrNone );
+    
+    TRACE_FUNC_EXIT;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoHasSyncHistory
+// This method returns ETrue if Data Store has history information. 
+// Slow-sync will be used if Data Store does not have history information.
+// -----------------------------------------------------------------------------
+TBool CContactsGrpDataStore::DoHasSyncHistory() const
+    {
+    TRACE_FUNC_ENTRY;
+    LOGGER_WRITE_1( "iHasHistory: %d", iHasHistory );
+    TRACE_FUNC_EXIT;  
+    return iHasHistory;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoAddedItems
+// This method returns UIDs of added items. Those items are added after previous
+// synchronization with current synchronization relationship. 
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CContactsGrpDataStore::DoAddedItems() const
+    {
+    TRACE_FUNC_ENTRY;
+    
+    // Clear new-items array
+    iNewItems->Reset();
+    if ( !iChangeFinder )
+        {
+        LOGGER_WRITE( "WARNING: Not ready, returns empty item list" );
+        return *iNewItems;      
+        }
+    
+    // Search for new items
+    TRAPD( error, iChangeFinder->FindNewItemsL( *iNewItems ) )
+    if ( error != KErrNone )
+        {
+        LOGGER_WRITE_1( "CContactsGrpDataStore::DoAddedItems, iChangeFinder->FindNewItemsL leaved with %d.", error );
+        } 
+    
+    LOGGER_WRITE_1( "New item count: %d.", iNewItems->ItemCount() );    
+    TRACE_FUNC_EXIT;
+    
+    return *iNewItems;  
+    }
+    
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoDeletedItems
+// Returns ids of items, which are deleted after previous synchronization
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CContactsGrpDataStore::DoDeletedItems() const
+    {
+    TRACE_FUNC_ENTRY;    
+    
+    // Clear deleted-items array
+    iDeletedItems->Reset();
+    if ( !iChangeFinder )
+        {
+        LOGGER_WRITE( "WARNING: Not ready, returns empty item list" );
+        return *iDeletedItems;      
+        }
+    
+    // Search for deleted items
+    TRAPD( error, iChangeFinder->FindDeletedItemsL( *iDeletedItems ) );
+    if ( error != KErrNone )
+        {
+        LOGGER_WRITE_1( "CContactsGrpDataStore::DoDeletedItems, iChangeFinder->FindDeletedItemsL leaved with %d.", error );
+        }
+    
+    LOGGER_WRITE_1( "Deleted item count: %d.", iDeletedItems->ItemCount() );
+    TRACE_FUNC_EXIT;
+    return *iDeletedItems;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoSoftDeletedItems
+// Not directly supported, empty list returned
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CContactsGrpDataStore::DoSoftDeletedItems() const
+    {
+    TRACE_FUNC;
+    // Return empty array as a result
+    return *iEmptyList;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoModifiedItems
+// Finds all modified items in the data store
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CContactsGrpDataStore::DoModifiedItems() const
+    {
+    TRACE_FUNC_ENTRY;
+    
+    // Clear updated-items array
+    iUpdatedItems->Reset();
+    if ( !iChangeFinder )
+        {
+        LOGGER_WRITE( "WARNING: Not ready, returns empty item list" );
+        return *iUpdatedItems;      
+        }
+    
+    // Search for updated items
+    TRAPD( error, iChangeFinder->FindChangedItemsL( *iUpdatedItems ) )
+    if ( error != KErrNone )
+        {
+        LOGGER_WRITE_1( "CContactsGrpDataStore::DoModifiedItems, iChangeFinder->FindChangedItemsL leaved with %d.", error );
+        }
+    
+    LOGGER_WRITE_1( "Modified item count: %d.", iUpdatedItems->ItemCount() );
+    TRACE_FUNC_EXIT;     
+    return *iUpdatedItems;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoMovedItems
+// Finds all moved items in the data store
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CContactsGrpDataStore::DoMovedItems() const
+    {
+    TRACE_FUNC;
+    // Moved items are not supported
+    // Return empty array as a result
+    return *iEmptyList;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoResetChangeInfoL
+// Resets change history in the data store. All content is considered
+// new in the data store point of view.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoResetChangeInfoL( TRequestStatus& aStatus )
+    {
+    TRACE_FUNC_ENTRY;
+    
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    // Check that we're in proper state
+    if ( iCurrentState != EOpenAndWaiting ) 
+        {
+        LOGGER_WRITE_1( "CContactsGrpDataStore::DoResetChangeInfoL, invalid state %d.", iCurrentState );
+        }   
+            
+    // Reset change info in ChangeFinder
+    iChangeFinder->ResetL();
+    iHasHistory = EFalse;
+    
+    // Signal we're done
+    User::RequestComplete( iCallerStatus, KErrNone );   
+        
+    TRACE_FUNC_EXIT;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCommitChangeInfoL
+// Commits change info. These items are no longer reported, when change
+// information is being queried.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCommitChangeInfoL( TRequestStatus& aStatus,
+    const MSmlDataItemUidSet& aItems )
+    {
+    TRACE_FUNC_ENTRY;
+    
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    // Ensure that we're in a proper state
+    if ( iCurrentState != EOpenAndWaiting ) 
+        {
+        LOGGER_WRITE_1( "CContactsGrpDataStore::DoCommitChangeInfoL, invalid state %d.", iCurrentState );
+        }
+
+    // Notify ChangeFinder
+    LOGGER_WRITE_1( "CContactsGrpDataStore::DoCommitChangeInfoL, item count %d.", aItems.ItemCount() );
+    iChangeFinder->CommitChangesL( aItems );
+    iHasHistory = ETrue;
+        
+    // Signal we're done
+    User::RequestComplete( iCallerStatus, KErrNone );
+    
+    TRACE_FUNC_EXIT;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCommitChangeInfoL
+// Commits change info. There is no more nothing to report when change
+// information is being queried. 
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCommitChangeInfoL( TRequestStatus& aStatus )
+    {
+    TRACE_FUNC_ENTRY;
+
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    // Ensure that we're in a proper state
+    if ( iCurrentState != EOpenAndWaiting ) 
+        {
+        LOGGER_WRITE_1( "CContactsGrpDataStore::DoCommitChangeInfoL, invalid state %d.", iCurrentState );
+        }
+    
+    // Notify ChangeFinder
+    iChangeFinder->CommitChangesL();
+    iHasHistory = ETrue;
+        
+    // Signal we're done
+    User::RequestComplete( iCallerStatus, KErrNone );
+        
+    TRACE_FUNC_EXIT;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::RegisterSnapshotL
+// Sets Changefinder to compare against current message store content
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::RegisterSnapshotL()
+    {
+    TRACE_FUNC_ENTRY;
+    
+    CArrayFixSeg<TSnapshotItem>* snapshot = 
+        new ( ELeave ) CArrayFixSeg<TSnapshotItem>( KSnapshotGranularity );
+    CleanupStack::PushL( snapshot );
+    
+    // Add everything to snapshot
+    
+    // GetGroupIdListL returns NULL if there are no groups in the database.
+    CContactIdArray* groups = iContactsDb->GetGroupIdListL();
+    if ( groups )
+        {
+        TKeyArrayFix key( iKey );
+        CleanupStack::PushL( groups );
+        for ( TInt i=0; i<groups->Count(); i++ )
+            {
+            CContactItem* item = iContactsDb->ReadContactLC((*groups)[i]);
+            CContactGroup* groupItem = static_cast<CContactGroup*>(item);
+            
+            LOGGER_WRITE_1( "Add group to snatshot, group id: %d",groupItem->Id() );
+            
+            TSnapshotItem snapshotItem( groupItem->Id() );
+            snapshotItem.CreateHashL( *groupItem );
+            
+            snapshot->InsertIsqL( snapshotItem, key );
+            
+            CleanupStack::PopAndDestroy( item );
+            }
+        CleanupStack::PopAndDestroy( groups );
+        }
+    
+    // Set new snapshot to compare against
+    iChangeFinder->SetNewSnapshot( snapshot );
+    
+    // Changefinder takes ownership of the snapshot
+    CleanupStack::Pop( snapshot );
+    
+    TRACE_FUNC_EXIT;
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::AssignString
+// Assigns data from one descriptor into another, truncates if too long 
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::AssignString( TDes8& aDestination, const TDesC8& aSource )
+    {
+    TInt targetLength = aSource.Length();
+    if ( aDestination.MaxLength() < targetLength )
+        {
+        targetLength = aDestination.MaxLength();
+        }
+        
+    aDestination.Copy( aSource.Ptr(), targetLength );
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::UpdateContactLastMod
+// Updates contact item last modified time. Contact sync will see item as changed one.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::UpdateContactLastMod( TContactItemId aContactId,
+        const TTime& aLastModified )
+    {
+    // Ignore errors.
+    // Cannot update timestamp if contact is already open. However modified time
+    // is most likely still updated by UI.
+    TRAP_IGNORE(
+        // OpenContactLX() places the edit lock on the cleanup stack
+        CContactItem* contact = iContactsDb->OpenContactLX( aContactId );
+        CleanupStack::PushL( contact );
+        contact->SetLastModified( aLastModified );
+        iContactsDb->CommitContactL( *contact );
+        CleanupStack::PopAndDestroy( contact );
+        CleanupStack::PopAndDestroy( 1 ); // lock object
+        );
+    }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::GroupNameExistsL
+// Checks does group name already exists.
+// -----------------------------------------------------------------------------
+TBool CContactsGrpDataStore::GroupNameExistsL( const TDesC& aLabel )
+    {
+    TBool duplicateFound( EFalse );
+    // GetGroupIdListL returns NULL if there are no groups in the database.
+    CContactIdArray* groups = iContactsDb->GetGroupIdListL();
+    if ( groups )
+        {
+        CleanupStack::PushL( groups );
+        for ( TInt i=0; i<groups->Count() && !duplicateFound; i++ )
+            {
+            CContactItem* item = iContactsDb->ReadContactLC((*groups)[i]);
+            CContactGroup* groupItem = static_cast<CContactGroup*>(item);
+            
+            if ( groupItem->HasItemLabelField() )
+                {
+                TPtrC label = groupItem->GetGroupLabelL();
+                if ( aLabel.Compare( label ) == 0 )
+                    {
+                    duplicateFound = ETrue;
+                    }
+                }
+            CleanupStack::PopAndDestroy( item );
+            }
+        CleanupStack::PopAndDestroy( groups );
+        }
+    
+    return duplicateFound;
+    }