phonebookengines/VirtualPhonebook/VPbkCntModel/src/CContact.cpp
changeset 0 e686773b3f54
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/phonebookengines/VirtualPhonebook/VPbkCntModel/src/CContact.cpp	Tue Feb 02 10:12:17 2010 +0200
@@ -0,0 +1,622 @@
+/*
+* 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:  The virtual phonebook contact
+*
+*/
+
+
+
+#include "CContact.h"
+#include <cntitem.h>
+#include <babitflags.h>
+
+#include <VPbkError.h>
+#include <MVPbkContactObserver.h>
+#include <TVPbkFieldTypeMapping.h>
+#include <MVPbkFieldType.h>
+#include <MVPbkContactStoreProperties.h>
+#include <CVPbkContactLinkArray.h>
+#include <CVPbkContactFieldCollection.h>
+#include <MVPbkStoreContactProperties.h>
+
+#include "CContactStore.h"
+#include "CFieldTypeMap.h"
+#include "TNewContactField.h"
+#include "CViewContact.h"
+#include "CContactLink.h"
+
+namespace VPbkCntModel {
+
+// ======== LOCAL CLASSES ========
+
+/**
+ * Internal store contact properties class,
+ * use CContactItem to implement MVPbkStoreContactProperties methods
+ *
+ */
+class CVPbkStoreContactProperties :
+    public CBase,
+    public MVPbkStoreContactProperties
+    {
+    public:
+        static CVPbkStoreContactProperties* NewL(
+        		CContactItem& aContactItem, 
+        		CContactDatabase& aContactDb );
+        
+        ~CVPbkStoreContactProperties();
+    protected: // MVPbkStoreContactProperties
+        TTime LastModifiedL() const;
+        TPtrC GuidL() const;
+    private:
+        CVPbkStoreContactProperties(
+        		CContactItem& aContactItem,
+        		CContactDatabase& aContactDb );
+
+    private:
+        CContactItem& iContactItem;  
+        CContactDatabase& iContactDb;
+    };
+    
+CVPbkStoreContactProperties* CVPbkStoreContactProperties::NewL(
+    CContactItem& aContactItem,
+    CContactDatabase& aContactDb )
+    {
+    CVPbkStoreContactProperties* self = new(ELeave) CVPbkStoreContactProperties(
+    		aContactItem,
+    		aContactDb );
+    return self;
+    }
+    
+CVPbkStoreContactProperties::CVPbkStoreContactProperties(
+    CContactItem& aContactItem, 
+	CContactDatabase& aContactDb ) :
+    iContactItem( aContactItem ),
+    iContactDb( aContactDb )
+    {
+    }
+    
+CVPbkStoreContactProperties::~CVPbkStoreContactProperties()
+    {
+    }
+    
+TTime CVPbkStoreContactProperties::LastModifiedL() const
+    {
+    return iContactItem.LastModified();
+    }
+
+TPtrC CVPbkStoreContactProperties::GuidL() const
+    {
+    return iContactItem.UidStringL(iContactDb.MachineId());
+    }
+// ======== LOCAL FUNCTIONS ========
+
+TInt FindMatchingField( const MVPbkBaseContactFieldCollection& aFields, 
+                       const MVPbkFieldType* aFieldType,
+                       const MVPbkFieldTypeList& aMasterFieldTypeList )
+    {
+    TInt result = KErrNotFound;
+    
+    const TInt fieldCount = aFields.FieldCount();
+    const TInt maxMatchPriority = aMasterFieldTypeList.MaxMatchPriority();
+    for ( TInt matchPriority = 0; matchPriority <= maxMatchPriority; 
+                    ++matchPriority )
+        {
+        for ( TInt i = 0; i < fieldCount; ++i )
+            {
+            const MVPbkFieldType* fieldType =
+                aFields.FieldAt( i ).MatchFieldType( matchPriority );
+            if ( fieldType && fieldType->IsSame(*aFieldType) )
+                { 
+                result = i;
+                break;
+                }
+            }
+        }
+        
+    return result;
+    }
+
+// ======== MEMBER FUNCTIONS ========
+
+inline CContact::CContact(
+        CContactStore& aParentStore, 
+        CContactItem& aContactItem,
+        TBool aIsNewContact ) :
+    iIsNewContact( aIsNewContact ),
+    iFields( *this, aContactItem.CardFields() ),
+    iParentStore( aParentStore ),
+    iLastUpdatedGroupContactId( KNullContactId )
+    {
+    }
+
+inline void CContact::ConstructL()
+    {
+    }
+
+CContact* CContact::NewL(
+        CContactStore& aParentStore, 
+        CContactItem* aContactItem,
+        TBool aIsNewContact /* = EFalse */)
+    {
+    CContact* self = new(ELeave) CContact( aParentStore, *aContactItem, 
+                                            aIsNewContact );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    // Potentially leaving initialisation is done; take parameter ownership now
+    self->iContactItem = aContactItem;
+    return self;
+    }
+
+CContact::~CContact()
+    { 
+    // Incase there was a problem when updating the time stamps of contacts
+    // belonging to a group we need to make sure the contact is unlocked by
+    // closing it. Symbian documentation says that despite the trailing L 
+    // in the function's name, CloseContactL cannot leave. Also specifying 
+    // a contact item that is not open, or cannot be found, is harmless.  
+   if ( iLastUpdatedGroupContactId != KNullContactId )
+       {
+       iParentStore.NativeDatabase().CloseContactL( iLastUpdatedGroupContactId );  
+       }
+          
+    iParentStore.ContactDestroyed( iContactItem, iModified );
+    delete iContactItem;
+    delete iAddedContacts;    
+    }
+
+void CContact::SetContact( CContactItem* aContactItem )
+    {
+    if ( aContactItem && aContactItem != iContactItem )
+        {
+        delete iContactItem;
+        iContactItem = aContactItem;
+        iFields.SetContact( *this, iContactItem->CardFields() );
+        }
+    }
+
+const CFieldTypeMap& CContact::FieldTypeMap() const
+    {
+    return iParentStore.FieldTypeMap();
+    }
+
+MVPbkObjectHierarchy& CContact::ParentObject() const
+    {
+    return iParentStore;
+    }
+
+const MVPbkStoreContactFieldCollection& CContact::Fields() const
+    {
+    return iFields;
+    }
+
+TBool CContact::IsSame( const MVPbkStoreContact& aOtherContact ) const
+    {
+    if ( &iParentStore == &aOtherContact.ContactStore() )
+        {
+        return ( iContactItem->Id() == 
+            static_cast<const CContact&>(aOtherContact).iContactItem->Id() );
+        }
+    return EFalse;
+    }
+
+TBool CContact::IsSame( const MVPbkViewContact& aOtherContact ) const
+    {
+    return aOtherContact.IsSame( *this, &ContactStore() );
+    }
+
+MVPbkContactLink* CContact::CreateLinkLC() const
+    {
+    return iParentStore.CreateLinkLC( iContactItem->Id() );
+    }
+
+void CContact::LockL(MVPbkContactObserver& aObserver) const
+    {
+    iParentStore.LockContactL( *this, aObserver );
+    }
+
+void CContact::DeleteL(MVPbkContactObserver& aObserver) const
+    {
+    iParentStore.NativeDatabase().CloseContactL( iContactItem->Id() );
+    iParentStore.DeleteContactL( iContactItem->Id(), aObserver );
+    }
+
+TBool CContact::MatchContactStore(const TDesC& aContactStoreUri) const
+    {
+    return iParentStore.MatchContactStore( aContactStoreUri );
+    }
+    
+TBool CContact::MatchContactStoreDomain( const TDesC& aContactStoreDomain ) const
+    {
+    return iParentStore.MatchContactStoreDomain( aContactStoreDomain );
+    }
+
+MVPbkContactBookmark* CContact::CreateBookmarkLC() const
+    {
+    return iParentStore.CreateBookmarkLC( iContactItem->Id() );
+    }
+    
+MVPbkContactStore& CContact::ParentStore() const
+    {
+    return iParentStore;
+    }
+
+MVPbkStoreContactFieldCollection& CContact::Fields()
+    {
+    return iFields;
+    }
+
+MVPbkStoreContactField* CContact::CreateFieldLC( const MVPbkFieldType& 
+                                                    aFieldType ) const
+    {
+    // Match the field type to the Contact Db's system template
+    CContactItemField* newField = iParentStore.CreateFieldLC( aFieldType );
+    if ( !newField )
+        {
+        User::Leave( KErrNotSupported );
+        }
+
+    // Create a wrapper for the newly created field
+    TNewContactField* fieldWrapper = 
+        new(ELeave) TNewContactField( const_cast<CContact&>(*this), newField );
+    CleanupStack::Pop( newField );
+    CleanupDeletePushL( fieldWrapper );
+
+    // Return the wrapper
+    return fieldWrapper;
+    }
+
+TInt CContact::AddFieldL( MVPbkStoreContactField* aField )
+    {
+    __ASSERT_ALWAYS( aField, VPbkError::Panic( VPbkError::ENullContactField ) );
+    __ASSERT_ALWAYS( &aField->ParentContact() == this, 
+        VPbkError::Panic(VPbkError::EInvalidContactField) );
+    // Test that the client doesn't pass an existing field of this contact as 
+    // a new one
+    __ASSERT_ALWAYS( aField != iFields.FieldPointer(), 
+        VPbkError::Panic(VPbkError::EInvalidContactField) );
+
+    // After all the checks the field can be cast back to the wrapper that was
+    // created in CreateFieldLC
+    TNewContactField* fieldWrapper = static_cast<TNewContactField*>( aField );
+    
+    // Add the Contact Model field to the contact item
+    iContactItem->AddFieldL( *fieldWrapper->NativeField() );
+    // Field added succesfully, release wrapper's ownership
+    fieldWrapper->ReleaseNativeField();
+
+    // Delete fieldWrapper. This function must not leave after deletion
+    // of fieldWrapper because CreateFieldLC has put fieldWrapper 
+    // into the cleanup stack and client pops it after this function
+    delete fieldWrapper;
+    // The field is appended to the contact -> return the last field index
+    return iContactItem->CardFields().Count() - 1;
+    }
+
+void CContact::RemoveField(TInt aIndex)
+    {
+    __ASSERT_ALWAYS( aIndex >= 0 && aIndex < iFields.FieldCount(), 
+        VPbkError::Panic(VPbkError::EInvalidFieldIndex) );
+    __ASSERT_ALWAYS( !iParentStore.StoreProperties().ReadOnly(),
+        VPbkError::Panic(VPbkError::EInvalidAccessToReadOnlyContact ) );
+
+    iContactItem->RemoveField( aIndex );
+    } 
+
+void CContact::RemoveAllFields()
+    {
+    __ASSERT_ALWAYS( !iParentStore.StoreProperties().ReadOnly(),
+        VPbkError::Panic(VPbkError::EInvalidAccessToReadOnlyContact ) );
+
+    iContactItem->CardFields().Reset();
+    }
+
+void CContact::CommitL( MVPbkContactObserver& aObserver ) const
+    {
+    iParentStore.CommitContactL( *this, aObserver );
+    }
+    
+MVPbkContactLinkArray* CContact::GroupsJoinedLC() const
+    {
+    CVPbkContactLinkArray* result = CVPbkContactLinkArray::NewLC();
+    
+    if ( iContactItem->Type() == KUidContactCard )
+        {
+        CContactCard* contactCard = static_cast<CContactCard*>( iContactItem );
+        CContactIdArray* groups = contactCard->GroupsJoinedLC();
+        const TInt count = groups->Count();
+        for ( TInt i = 0; i < count; ++i )
+            {
+            MVPbkContactLink* link = iParentStore.CreateLinkLC( (*groups)[i] );
+            result->AppendL( link );
+            CleanupStack::Pop(); // link
+            }
+        CleanupStack::PopAndDestroy( groups );
+        }
+    
+    return result;
+    }
+
+TInt CContact::MaxNumberOfFieldL( const MVPbkFieldType& aType ) const
+    {
+    if ( iParentStore.StoreProperties().SupportedFields().ContainsSame(aType) )
+        {
+        return KVPbkStoreContactUnlimitedNumber;
+        }
+    return 0;
+    }
+        
+MVPbkContactGroup* CContact::Group()
+    {
+    MVPbkContactGroup* result = NULL;
+    
+    if ( iContactItem->Type() == KUidContactGroup )
+        {
+        result = this;
+        }
+        
+    return result;
+    }
+
+void CContact::SetGroupLabelL( const TDesC& aLabel )
+    {
+    TVPbkFieldTypeMapping typeMapping;
+    typeMapping.SetNonVersitType( EVPbkNonVersitTypeGenericLabel );
+    const MVPbkFieldType* labelType = 
+            typeMapping.FindMatch( iParentStore.MasterFieldTypeList() );
+    
+    TInt labelFieldIndex = FindMatchingField( Fields(), 
+                                             labelType, 
+                                             iParentStore.MasterFieldTypeList() );
+
+    // Update the timestamp of the all contacts that belong to the group. 
+    // This is needed for synch service as it checks the timestamps of contacts 
+    // and get's the group information from the vCard of a contact. 
+    UpdateTimeStampOfAllContactsInGroupL();
+       
+    if ( labelFieldIndex != KErrNotFound )
+        {
+        // field already exists
+        MVPbkStoreContactField& labelField = Fields().FieldAt( labelFieldIndex );
+        MVPbkContactFieldTextData::Cast( labelField.FieldData()).SetTextL(aLabel );
+        }
+    else
+        {
+        // field does not exist => add it
+        MVPbkStoreContactField* labelField = CreateFieldLC( *labelType );
+        MVPbkContactFieldTextData::Cast( labelField->FieldData() ).SetTextL( aLabel );
+        AddFieldL( labelField );
+        CleanupStack::Pop(); // labelField
+        }
+    }
+    
+TPtrC CContact::GroupLabel() const
+    {
+    TVPbkFieldTypeMapping typeMapping;
+    typeMapping.SetNonVersitType( EVPbkNonVersitTypeGenericLabel );
+    const MVPbkFieldType* labelType = 
+            typeMapping.FindMatch( iParentStore.MasterFieldTypeList() );
+    
+    TInt labelFieldIndex = FindMatchingField( Fields(), 
+                                             labelType, 
+                                             iParentStore.MasterFieldTypeList() );
+    
+    if ( labelFieldIndex != KErrNotFound )
+        {
+        const MVPbkBaseContactField& labelField = Fields().FieldAt( labelFieldIndex );
+        return MVPbkContactFieldTextData::Cast(labelField.FieldData()).Text();
+        }
+    else
+        {
+        return KNullDesC();        
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CContact::UpdateTimeStampOfAllContactsInGroupL
+// --------------------------------------------------------------------------
+//
+void CContact::UpdateTimeStampOfAllContactsInGroupL( )
+    {
+    MVPbkContactLinkArray* contactsInGroup = ItemsContainedLC();
+    
+    // Loop through all contacts in the group and update time stamps
+    for (TInt i=0; i < contactsInGroup->Count(); i++ )
+        {
+        UpdateTimeStampOfContactInGroupL( contactsInGroup->At(i) );
+        }
+    
+    CleanupStack::PopAndDestroy(); // contactsInGroup
+    }
+
+// --------------------------------------------------------------------------
+// CContact::UpdateTimeStampOfContactInGroupL
+// --------------------------------------------------------------------------
+//
+void CContact::UpdateTimeStampOfContactInGroupL(const MVPbkContactLink& aContactLink )
+    {
+    const CContactLink& link = static_cast<const CContactLink&>( aContactLink );
+    
+    // Store the id of currently processed contact in the group
+    iLastUpdatedGroupContactId = link.ContactId();
+    
+    // Try to open the contact for editing and then commit.
+    // This will update the timestamp of the contact.
+    // In case of a leave, we will make sure in the destructor that the
+    // contact is unlocked (see CContact::~CContact).
+    CContactItem* contact = 
+          iParentStore.NativeDatabase().OpenContactL( link.ContactId() );  
+    CleanupStack::PushL(contact);     
+    
+    iParentStore.NativeDatabase().CommitContactL(*contact);
+    CleanupStack::PopAndDestroy(contact);
+    
+    // No need to store the id anymore.
+    iLastUpdatedGroupContactId = KNullContactId;
+    }
+
+void CContact::AddContactL( const MVPbkContactLink& aContactLink )
+    {        
+    // We have to maintain iAddedContacts ID array here because
+    // the AddContactToGroup(id, id) method does not update
+    // the native contact group we have in hand. It updates the
+    // database only. So, in the ItemsContainedLC function
+    // we have to know both the group members in the database
+    // and the group members that have been added after this group
+    // has been read from database
+    const CContactLink& link = static_cast<const CContactLink&>( aContactLink );
+    
+    // Read the contact so that 
+    // AddContactToGroupL(CContactItem &aItem, CContactItem &aGroup)
+    // can be used.
+    CContactItem* contact = 
+        iParentStore.NativeDatabase().ReadContactLC( link.ContactId() );
+        
+    if ( !iAddedContacts )
+        {
+        iAddedContacts = CContactIdArray::NewL();
+        }        
+    if ( iAddedContacts->Find( link.ContactId() ) == KErrNotFound )
+        {
+        iAddedContacts->AddL( link.ContactId() );
+        }
+    
+    // Use AddContactToGroupL(CContactItem &aItem, CContactItem &aGroup)
+    // instead of 
+    // AddContactToGroupL(TContactItemId aItemId, TContactItemId aGroupId)
+    // because otherwise the member iContactItem won't be updated and commiting
+    // it would loose the information about added contact
+    TRAPD( err1, iParentStore.NativeDatabase().AddContactToGroupL(
+               *contact, *iContactItem ) );
+    if ( err1 != KErrNone )
+        {
+        iAddedContacts->Remove( iAddedContacts->Count() - 1 );
+        User::Leave( err1 );
+        } 
+    
+    // Update the timestamp of the added contact. This is needed for 
+    // synch service as it checks the timestamps of contacts and get's the group
+    // information from the vCard of a contact.    
+    TRAPD( err2, UpdateTimeStampOfContactInGroupL( aContactLink ) );
+    if ( err2 != KErrNone )
+        {
+        iAddedContacts->Remove( iAddedContacts->Count() - 1 );
+        iParentStore.NativeDatabase().RemoveContactFromGroupL(
+                *contact, *iContactItem );
+        User::Leave( err2 );
+        } 
+    CleanupStack::PopAndDestroy( contact );
+    }
+    
+void CContact::RemoveContactL( const MVPbkContactLink& aContactLink )
+    {    
+    const CContactLink& link = static_cast<const CContactLink&>( aContactLink );
+    
+    // Read the contact so that 
+    // RemoveContactFromGroupL(CContactItem &aItem, CContactItem &aGroup)
+    // can be used.
+    CContactItem* contact = 
+        iParentStore.NativeDatabase().ReadContactLC( link.ContactId() );
+    
+    // First update the timestamp of the removed contact. This is needed for 
+    // synch service as it checks the timestamps of contacts and get's the group
+    // information from the vCard of a contact. If updating fails this function 
+    // will leave and contact won't be removed from the group.
+    UpdateTimeStampOfContactInGroupL(aContactLink);
+    
+    TInt index = KErrNotFound;
+    if ( iAddedContacts )
+    	{
+    	index = iAddedContacts->Find( link.ContactId() );
+    	}
+    
+    // Use RemoveContactFromGroupL(CContactItem &aItem, CContactItem &aGroup)
+    // instead of 
+    // RemoveContactFromGroupL(TContactItemId aItemId, TContactItemId aGroupId)
+    // because otherwise the member iContactItem won't be updated and commiting
+    // it would loose the information about removed contact
+    iParentStore.NativeDatabase().RemoveContactFromGroupL(
+            *contact, *iContactItem );
+    if ( index != KErrNotFound )
+        {
+        iAddedContacts->Remove( index );
+        }
+    CleanupStack::PopAndDestroy( contact );
+    }
+
+MVPbkContactLinkArray* CContact::ItemsContainedLC() const
+    {
+    CVPbkContactLinkArray* result = CVPbkContactLinkArray::NewLC();
+    TInt i;
+
+    const CContactGroup* thisGroup = static_cast<const CContactGroup*>( iContactItem );
+    // 1. append the IDs found in the group
+    const CContactIdArray* contacts = thisGroup->ItemsContained();
+    const TInt count = ( contacts ? contacts->Count() : 0 );
+    for ( i = 0; i < count; ++i )
+        {
+        MVPbkContactLink* link = iParentStore.CreateLinkLC( (*contacts)[i] );
+        result->AppendL( link );
+        CleanupStack::Pop(); // link
+        }
+    // 2. append the IDs in iAddedContacts
+    const TInt addedCount = ( iAddedContacts ? iAddedContacts->Count() : 0 );
+    for ( i = 0; i < addedCount; ++i )
+        {
+        // add the contact if the iAddedContact[i] was not in contacts already
+        if ( !contacts ||
+            contacts->Find( (*iAddedContacts)[i]) == KErrNotFound )
+            {
+            MVPbkContactLink* link = iParentStore.CreateLinkLC( (*iAddedContacts)[i] );
+            result->AppendL( link );
+            CleanupStack::Pop(); // link
+            }
+        }
+    return result;
+    }
+
+TAny* CContact::StoreContactExtension(TUid aExtensionUid) 
+{
+    if( aExtensionUid == KMVPbkStoreContactExtension2Uid )
+		return static_cast<MVPbkStoreContact2*>( this );
+    return NULL;
+}
+    
+MVPbkStoreContactProperties* CContact::PropertiesL() const
+    {
+    return CVPbkStoreContactProperties::NewL( *iContactItem, iParentStore.NativeDatabase() );
+    }
+
+void CContact::SetAsOwnL(MVPbkContactObserver& aObserver) const
+	{
+	iParentStore.SetAsOwnL( *this, aObserver );
+	}
+
+TAny* CContact::BaseContactExtension( TUid aExtensionUid )
+    {
+    if( aExtensionUid == KVPbkBaseContactExtension2Uid )
+        return static_cast<MVPbkBaseContact2*>( this );
+    return NULL;
+    }
+
+TBool CContact::IsOwnContact( TInt& aError ) const
+    {
+    aError = KErrNone;
+    return ( iContactItem->Type() == KUidContactOwnCard );
+    }
+
+
+} // namespace VPbkCntModel
+
+// end of file