phonebookui/Phonebook2/USIMExtension/src/CPsu2SimContactProcessor.cpp
changeset 0 e686773b3f54
child 58 d4f567ce2e7c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/phonebookui/Phonebook2/USIMExtension/src/CPsu2SimContactProcessor.cpp	Tue Feb 02 10:12:17 2010 +0200
@@ -0,0 +1,1010 @@
+/*
+* 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:  A class that processes sim contacts into appropritate form for
+*                the copying process.
+*                Handles errors related to SIM contact fields
+*
+*/
+
+
+// INCLUDE FILES
+#include "CPsu2SimContactProcessor.h"
+
+// Phonebook 2
+#include "Pbk2USimUI.hrh"
+#include "CPsu2CopyToSimFieldInfoArray.h"
+#include "CPsu2CharConv.h"
+#include <MPbk2FieldProperty.h>
+#include <MPbk2ContactNameFormatter.h>
+#include <Pbk2ContactFieldCopy.h>
+
+// Virtual Phonebook
+#include <TVPbkFieldTypeMapping.h>
+#include <CVPbkContactManager.h>
+#include <MVPbkContactStore.h>
+#include <MVPbkFieldType.h>
+#include <MVPbkBaseContact.h>
+#include <MVPbkStoreContact.h>
+#include <MVPbkStoreContactField.h>
+#include <MVPbkContactStoreProperties.h>
+#include <MVPbkContactFieldTextData.h>
+#include <CVPbkFieldTypeRefsList.h>
+#include <CVPbkContactFieldIterator.h>
+#include <CVPbkFieldTypeRefsList.h>
+#include <VPbkEng.rsg>
+
+// System includes
+#include <gsmerror.h>
+#include <exterror.h>
+#include <barsread.h>
+#include <coemain.h>
+#include <featmgr.h>
+
+// Debugging headers
+#include <Pbk2Debug.h>
+
+
+/// Unnamed namespace for local definitions
+namespace {
+
+#ifdef _DEBUG
+enum TPanicCode
+    {
+    EPreCond_SplitToSimContactsL
+    };
+
+void Panic(TInt aReason)
+    {
+    _LIT( KPanicText, "CPsu2SimContactProcessor");
+    User::Panic( KPanicText, aReason );
+    }
+
+#endif // _DEBUG
+
+/**
+ * A helper class to keep track of the number fields of certain type
+ */
+class TFieldTypeCounter
+    {
+    public: // Construction
+
+        /**
+         * Constructor.
+         *
+         * @param aType     Field type.
+         */
+        TFieldTypeCounter(
+                const MVPbkFieldType& aType ) :
+                    iType( aType ), iCounter( 0 )
+            {}
+
+    public: // Data
+        // Ref: Field type
+        const MVPbkFieldType& iType;
+        // Own: Number of fields
+        TInt iCounter;
+    };
+
+
+/**
+ * Returns matching field type counter object based on given field type.
+ *
+ * @param aArray    Array of field type counters.
+ * @param aType     Field type of interest.
+ * @return  Matching field type counter.
+ */
+TFieldTypeCounter& FieldTypeCounterL( 
+        RArray<TFieldTypeCounter>& aArray, const MVPbkFieldType& aType )
+    {
+    const TInt count = aArray.Count();
+    for (TInt i = 0; i < count; ++i)
+        {
+        if (aArray[i].iType.IsSame(aType))
+            {
+            return aArray[i];
+            }
+        }
+    aArray.AppendL(TFieldTypeCounter(aType));
+
+    return aArray[count];
+    }
+
+/**
+ * Checks if the contact already has maximum amount fields
+ * of given field type.
+ *
+ * @param aMaxAmountFieldInContact  Max number of allowed fields of type.
+ * @param aType                     Field type.
+ * @param aArray                    Array of field type counters.
+ * @return  ETrue if contact already has max amount of fields of given type.
+ */
+TBool CheckNumberOfFieldsL( 
+        TInt aMaxAmountFieldInContact, const MVPbkFieldType& aType,
+        RArray<TFieldTypeCounter>& aArray )
+    {
+    TBool ret( EFalse );
+
+    TFieldTypeCounter& counter = FieldTypeCounterL( aArray, aType );
+
+    // Compare the max amount fields to the current amount of the
+    // fields in the contact
+    if (counter.iCounter >= aMaxAmountFieldInContact)
+        {
+        ret = ETrue;
+        }
+
+    // Add one to counter of the given type
+    ++counter.iCounter;
+
+    return ret;
+    }
+
+/**
+ * Checks is given field type a number type.
+ *
+ * @param aSimType  Field type.
+ * @return  ETrue if the field is of number type.
+ */
+TBool IsNumberType( const MVPbkFieldType& aSimType )
+    {
+    TBool ret = EFalse;
+
+    // SIM number type is always Mobile phone (general) -> EVPbkVersitNameTEL,
+    // therefore a selector is not needed
+    TArray<TVPbkFieldVersitProperty> props = aSimType.VersitProperties();
+    if ( props.Count() > 0 && props[0].Name() == EVPbkVersitNameTEL )
+        {
+        ret = ETrue;
+        }
+
+    return ret;
+    }
+
+/**
+ * Returns a valid number, removes e.g spaces, braces...
+ *
+ * @param aSource           Source descriptor.
+ * @param aNumberKeyMap     Number key mapping.
+ * @return  Formatted valid number.
+ */
+HBufC* CreateValidNumberLC( 
+        const TDesC& aSource, const TDesC& aNumberKeyMap )
+    {
+    const TInt length = aSource.Length();
+    HBufC* number = HBufC::NewLC( length );
+    TPtr ptr( number->Des() );
+    for ( TInt i = 0; i < length; ++i )
+        {
+        if ( aNumberKeyMap.Locate( aSource[i] ) != KErrNotFound )
+            {
+            ptr.Append( aSource[i] );
+            }
+        }
+    return number;
+    }
+
+} /// namespace
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::CPsu2SimContactProcessor
+// --------------------------------------------------------------------------
+//
+CPsu2SimContactProcessor::CPsu2SimContactProcessor( 
+        MVPbkContactStore& aTargetStore,
+        CPsu2CopyToSimFieldInfoArray& aCopyToSimFieldInfoArray,
+        MPbk2ContactNameFormatter& aNameFormatter,
+        const MVPbkFieldTypeList& aMasterFieldTypeList )
+        :   iTargetStore( aTargetStore ),
+            iCopyToSimFieldInfoArray( aCopyToSimFieldInfoArray ),
+            iNameFormatter( aNameFormatter ),
+            iMasterFieldTypeList( aMasterFieldTypeList ),
+            iSimMaxMatchPriority( 
+                aTargetStore.StoreProperties().SupportedFields().
+                  MaxMatchPriority() )
+    {
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::~CPsu2SimContactProcessor
+// --------------------------------------------------------------------------
+//
+CPsu2SimContactProcessor::~CPsu2SimContactProcessor()
+    {
+    iNewSimContacts.ResetAndDestroy();
+    iIncludedTypes.Close();
+    delete iCharConvUcs2;
+    delete iCharConvSms7Bit;
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::NewL
+// --------------------------------------------------------------------------
+//
+CPsu2SimContactProcessor* CPsu2SimContactProcessor::NewL( 
+        MVPbkContactStore& aTargetStore,
+        CPsu2CopyToSimFieldInfoArray& aCopyToSimFieldInfoArray,
+        MPbk2ContactNameFormatter& aNameFormatter,
+        const MVPbkFieldTypeList& aMasterFieldTypeList,
+        RFs& aFs )
+    {
+    CPsu2SimContactProcessor* self = new( ELeave ) CPsu2SimContactProcessor
+        ( aTargetStore, aCopyToSimFieldInfoArray, aNameFormatter,
+          aMasterFieldTypeList );
+    CleanupStack::PushL( self );
+    self->ConstructL( aFs );
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::ConstructL
+// --------------------------------------------------------------------------
+//
+void CPsu2SimContactProcessor::ConstructL( RFs& aFs )
+    {
+    const TInt count = iCopyToSimFieldInfoArray.Count();
+    for ( TInt i = 0; i < count; ++i )
+        {
+        iIncludedTypes.AppendL( 
+            &iCopyToSimFieldInfoArray[i].SourceType() );
+        }
+
+
+    iCharConvUcs2 = CPsu2CharConv::NewL( aFs, KCharacterSetIdentifierUcs2 );
+    // Symbian character converter uses CR/LF by default. (U)SIM uses CR.
+    // (JustLineFeed is ok here because the converter is used just to check
+    // SIM conversion lengths)
+    iCharConvUcs2->SetDownGradeLf( CCnvCharacterSetConverter::
+            EDowngradeExoticLineTerminatingCharactersToJustLineFeed );
+
+    iCharConvSms7Bit = CPsu2CharConv::NewL( aFs, KCharacterSetIdentifierSms7Bit );
+    iCharConvSms7Bit->SetDownGradeLf( CCnvCharacterSetConverter::
+            EDowngradeExoticLineTerminatingCharactersToJustLineFeed );
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::HandleSimError
+// --------------------------------------------------------------------------
+//
+TBool CPsu2SimContactProcessor::HandleSimError( TInt aError )
+    {
+    TBool result = EFalse;
+    switch (aError)
+        {
+        case KErrGsmSimServAnrFull:
+            {
+            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+            ("CPsu2SimContactProcessor::HandleSimError KErrGsmSimServAnrFull"));
+
+            // There might be several EF ANR files in USIM. Save the number
+            // of error messages and use it later to check how many numbers
+            // can be put to one contact
+            ++iNumOfAdditionalNumberErrors;
+            result = ETrue;
+            break;
+            }
+        case KErrGsmSimServEmailFull:
+            {
+            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+                ("CPsu2SimContactProcessor::HandleSimError KErrGsmSimServEmailFull"));
+
+            // SIM contact can not have emails anymore because EF(email) is full
+            iSimErrors |= KPsu2EMailFullError;
+            RemoveFieldTypesFromIncludedTypes( KPsu2EMailFullError );
+            result = ETrue;
+            break;
+            }
+        case KErrGsmSimServSneFull:
+            {
+            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+                ("CPsu2SimContactProcessor::HandleSimError KErrGsmSimServSneFull"));
+            iSimErrors |= KPsu2SecondNameFullError;
+            RemoveFieldTypesFromIncludedTypes( KPsu2SecondNameFullError );
+            result = ETrue;
+            break;
+            }
+        default:
+            {
+            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+                ("CPsu2SimContactProcessor::HandleSimError unhandled error code %d"),
+                    aError );
+            break;
+            }
+        }
+    return result;
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::CreateSimContactsL
+//
+// This called to create SIM contacts from a source contact
+// The copying logic:
+//  1) Create a SIM contact and add all the fields from the source
+//     that possibly can be copied.
+//  2) If the created SIM contact has too many fields for one SIM contact,
+//     split the SIM contact. If the splitted contact has still too many
+//     fields for one SIM contact split the splitted contact. This is
+//     continued until the splitted contact doesn't need to be splitted
+//     again.
+// --------------------------------------------------------------------------
+//
+void CPsu2SimContactProcessor::CreateSimContactsL( 
+        MVPbkStoreContact& aSourceContact,
+        RPointerArray<MVPbkStoreContact>& aSimContacts )
+    {
+    iNewSimContacts.ResetAndDestroy();
+    // Create a target contact
+    MVPbkStoreContact* contact = iTargetStore.CreateNewContactLC();
+    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+        ("CPsu2SimContactProcessor::CreateSimContactsL target contact created"));
+
+    // Add name to the new contact
+    AddNameFieldsL(aSourceContact, *contact);
+    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+        ("CPsu2SimContactProcessor::CreateSimContactsL name fields added"));
+
+    // Append fields that can possible be copied to the SIM
+    AppendSupportedFieldsL(aSourceContact, *contact);
+    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+        ("CPsu2SimContactProcessor::CreateSimContactsL supported fields added"));
+
+    // Split the contact
+    CleanupStack::Pop(); // contact
+    SplitToSimContactsL(contact);
+    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+        ("CPsu2SimContactProcessor::CreateSimContactsL contact splitted"));
+
+    // Contacts that have only name are not copied to SIM
+    RemoveContactsThatHaveOnlyNameL();
+    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+        ("CPsu2SimContactProcessor::CreateSimContactsL name only contacts removed"));
+
+    const TInt firstPos = 0;
+    const TInt count = iNewSimContacts.Count();
+    for ( TInt i = count - 1; i >= 0; --i )
+        {
+        aSimContacts.InsertL( iNewSimContacts[i], firstPos );
+        PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+            ("CPsu2SimContactProcessor::CreateSimContactsL contact added to new SIM contacts"));
+
+        iNewSimContacts.Remove( i );
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::CreateFixedSimContactsL
+//
+// This is called when saving a SIM contact has failed and HandleSimError
+// has handled the error. It means that the state of this processor has
+// changed and the failed SIM contact can be splitted or there are fields
+// that must be removed from the failed contact. After this the resulted
+// SIM contacts will be copied again.
+// --------------------------------------------------------------------------
+//
+void CPsu2SimContactProcessor::CreateFixedSimContactsL( 
+        MVPbkStoreContact& aSimContact,
+        RPointerArray<MVPbkStoreContact>& aSimContacts )
+    {
+    iNewSimContacts.ResetAndDestroy();
+    // Create a target contact
+    MVPbkStoreContact* contact = iTargetStore.CreateNewContactLC();
+    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+        ("CPsu2SimContactProcessor::CreateFixedSimContactsL target contact created"));
+
+    // Add name to the new contact
+    AddNameFieldsL(aSimContact, *contact);
+    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+        ("CPsu2SimContactProcessor::CreateFixedSimContactsL name fields added"));
+
+    // Append fields that can possible be copied to the SIM
+    AppendSupportedFieldsL(aSimContact, *contact);
+    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+        ("CPsu2SimContactProcessor::CreateFixedSimContactsL supported fields added"));
+
+    const TInt firstPos = 0;
+    const TInt newCount = contact->Fields().FieldCount();
+    if (newCount > 0 && newCount != aSimContact.Fields().FieldCount())
+        {
+        // After appending supported fields the amount of field is different
+        // than in the original contact. This means that there has been
+        // an error that has changed the included types list.
+        if ( IsValidContactToSaveL( *contact ) )
+            {
+            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+                ("CPsu2SimContactProcessor::CreateFixedSimContactsL is valid contact to save"));
+
+            aSimContacts.InsertL(contact, firstPos);
+            CleanupStack::Pop(); // contact
+
+            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+                ("CPsu2SimContactProcessor::CreateFixedSimContactsL contact added to new SIM contacts"));
+            }
+        else
+            {
+            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+                ("CPsu2SimContactProcessor::CreateFixedSimContactsL is not valid contact to save"));
+
+            CleanupStack::PopAndDestroy(); // contact
+            }
+        }
+    else
+        {
+        // Split the contact
+        CleanupStack::Pop(); // contact
+        // Takes ownership of the contact
+        if ( SplitToSimContactsL( contact ) )
+            {
+            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+                ("CPsu2SimContactProcessor::CreateFixedSimContactsL contact splitted"));
+
+            // Contacts that have only name are not copied to SIM
+            RemoveContactsThatHaveOnlyNameL();
+            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+                ("CPsu2SimContactProcessor::CreateFixedSimContactsL name only contacts removed"));
+
+            const TInt count = iNewSimContacts.Count();
+            for (TInt i = count - 1; i >= 0; --i)
+                {
+                aSimContacts.InsertL(iNewSimContacts[i], firstPos);
+                iNewSimContacts.Remove(i);
+
+                PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+                    ("CPsu2SimContactProcessor::CreateFixedSimContactsL contact added to new SIM contacts"));
+                }
+            }
+        else
+            {
+            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+                ("CPsu2SimContactProcessor::CreateFixedSimContactsL contact does not split"));
+
+            iNewSimContacts.ResetAndDestroy();
+            }
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::CreateFixedSimContactsL
+// --------------------------------------------------------------------------
+//
+TBool CPsu2SimContactProcessor::DetailsDropped()
+    {
+    TBool ret( EFalse );
+    ret = iSimErrors & KPsu2EMailFullError;
+    if ( !ret  )
+        {
+        ret = iSimErrors & KPsu2SecondNameFullError;
+        }
+    return ret;
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::RemoveFieldTypesFromIncludedTypes
+//
+// Removes error related field types from the included types so those
+// types won't be copied to SIM contact anymore.
+// --------------------------------------------------------------------------
+//
+void CPsu2SimContactProcessor::RemoveFieldTypesFromIncludedTypes( 
+        TPsu2ErrorCode aBlockingError )
+    {
+    const TInt includedCount = iIncludedTypes.Count();
+    for ( TInt i = includedCount - 1; i >= 0; --i )
+        {
+        const TPsu2CopyToSimFieldInfo* info = 
+            iCopyToSimFieldInfoArray.FindInfoForSourceType( 
+                *iIncludedTypes[i] );
+        if ( info && info->BlockedByError( aBlockingError ) )
+            {
+            iIncludedTypes.Remove( i );
+            }
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::AddNameFieldsL
+//
+// Adds name fields from source contact to the target.
+// --------------------------------------------------------------------------
+//
+void CPsu2SimContactProcessor::AddNameFieldsL( 
+        MVPbkStoreContact& aSource, MVPbkStoreContact& aTarget )
+    {
+    // Copy formatted name always to SIM's name field
+    CopyTitleFieldDataL( aSource, aTarget,
+        iCopyToSimFieldInfoArray.SimNameType() );
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::CopyReadingFieldsL
+//
+// Copies reading fields in Japanese variants.
+// --------------------------------------------------------------------------
+//
+void CPsu2SimContactProcessor::CopyReadingFieldsL( 
+        MVPbkStoreContact& aSource, MVPbkStoreContact& aTarget )
+    {
+    // In case the name can be built without first name reading
+    // and last name reading, the last name reading and first name reading
+    // field data is combined according to name formatting rules and
+    // the formatted name Reading is copied to Second name field
+    // in Japanese variants.
+
+    const MVPbkFieldTypeList& supportedTypes =
+        iTargetStore.StoreProperties().SupportedFields();
+    if ( supportedTypes.ContainsSame(
+         iCopyToSimFieldInfoArray.LastNameReadingType() ) )
+        {
+        CVPbkFieldTypeRefsList* list = CVPbkFieldTypeRefsList::NewL();
+        CleanupStack::PushL( list );
+        // Get fields that actually a part of the formatted name
+        CVPbkBaseContactFieldTypeListIterator* itr =
+            iNameFormatter.ActualTitleFieldsLC( *list, aSource.Fields() );
+        // Check if the title has reading fields
+        TBool containsReading = EFalse;
+        while ( itr->HasNext() )
+            {
+            TInt typeId =
+                itr->Next()->BestMatchingFieldType()->FieldTypeResId();
+            if ( typeId == R_VPBK_FIELD_TYPE_LASTNAMEREADING ||
+                 typeId == R_VPBK_FIELD_TYPE_FIRSTNAMEREADING )
+                {
+                containsReading = ETrue;
+                }
+            }
+        CleanupStack::PopAndDestroy(2, list);
+
+        // If title doesn't contain reading fields then copy formatted
+        // reading to SIM's reading (=second name) field
+        if ( !containsReading )
+            {
+            // Create a temp contact from source store for getting
+            // field collection of reading fields
+            MVPbkStoreContact* tmpCnt =
+                aSource.ParentStore().CreateNewContactLC();
+            MVPbkStoreContactFieldCollection& sourceFields =
+                aSource.Fields();
+            const TInt fieldCount = sourceFields.FieldCount();
+            for ( TInt i = 0; i < fieldCount; ++i )
+                {
+                const MVPbkFieldType* sourceType =
+                    sourceFields.FieldAt( i ).BestMatchingFieldType();
+                if ( sourceType )
+                    {
+                    TInt typeId = sourceType->FieldTypeResId();
+                    if ( typeId == R_VPBK_FIELD_TYPE_LASTNAMEREADING ||
+                         typeId == R_VPBK_FIELD_TYPE_FIRSTNAMEREADING )
+                        {
+                        Pbk2ContactFieldCopy::CopyFieldL
+                            ( sourceFields.FieldAt( i ),
+                              *sourceType, *tmpCnt );
+                        }
+                    }
+                }
+            if ( tmpCnt->Fields().FieldCount() > 0 )
+                {
+                // If there were reading fields in the source then copy
+                // the formatted reading to SIM's last name reading
+                CopyTitleFieldDataL( *tmpCnt, aTarget,
+                    iCopyToSimFieldInfoArray.LastNameReadingType() );
+                }
+            CleanupStack::PopAndDestroy(); // tmpCnt
+            }
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::AppendSupportedFieldsL
+//
+// Appends fields that are in included properties and supported
+// by the SIM store.
+// --------------------------------------------------------------------------
+//
+void CPsu2SimContactProcessor::AppendSupportedFieldsL( 
+        MVPbkBaseContact& aSource, MVPbkStoreContact& aTarget)
+    {
+    const MVPbkBaseContactFieldCollection& fields = aSource.Fields();
+    const TInt fieldCount = fields.FieldCount();
+    const TInt typeCount = iIncludedTypes.Count();
+    const TInt maxPriority = iMasterFieldTypeList.MaxMatchPriority();
+    const MVPbkFieldTypeList& supportedTypes =
+        iTargetStore.StoreProperties().SupportedFields();
+
+    TBool contactCopyFailed = EFalse;
+    for (TInt i = 0; i < fieldCount && !contactCopyFailed; ++i)
+        {
+        // Get the source field
+        const MVPbkBaseContactField& field = fields.FieldAt(i);
+        // Get the source field type
+        const MVPbkFieldType* type = field.BestMatchingFieldType();
+
+        TBool fieldCopied = EFalse;
+        for (TInt j = 0; j < typeCount && type && !fieldCopied
+                && !contactCopyFailed; ++j)
+            {
+            // Check if the field is one of the fields that can be copied
+            // to the SIM
+            if (type->IsSame(*iIncludedTypes[j]))
+                {
+                // The field possible can be copied to the SIM
+                // Get the target(=SIM) field type for source type
+                const MVPbkFieldType* simType =
+                    iCopyToSimFieldInfoArray.ConvertToSimType(*type);
+                // Check if the sim store supports the converted field
+                if (simType && supportedTypes.ContainsSame(*simType))
+                    {
+                    if ( !CopyFieldL(field, aTarget, *simType) )
+                        {
+                        // If copying of one field fails then copying
+                        // the contact fails
+                        contactCopyFailed = ETrue;
+
+                        PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
+                            ("CPsu2SimContactProcessor::AppendSupportedFieldsL contact copy failed"));
+                        }
+                    fieldCopied = ETrue;
+                    }
+                }
+            }
+        }
+
+    if ( contactCopyFailed )
+        {
+        // Remove all fields from the target contact
+        aTarget.RemoveAllFields();
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::SplitToSimContactsL
+//
+// Tries to split the source contact, return ETrue if splitted.
+// --------------------------------------------------------------------------
+//
+TBool CPsu2SimContactProcessor::SplitToSimContactsL( 
+        MVPbkStoreContact* aSourceContact )
+    {
+    __ASSERT_DEBUG(iNewSimContacts.Count() == 0,
+        Panic(EPreCond_SplitToSimContactsL));
+
+    MVPbkStoreContact* contact = aSourceContact;
+    CleanupDeletePushL(contact);
+    TBool result = EFalse;
+
+    MVPbkStoreContact* splitted = SplitContactLC(*contact);
+    if (splitted)
+        {
+        // Contact is splitted at least into two contacts
+        iNewSimContacts.AppendL(contact);
+        CleanupStack::Pop(2); // contact, splitted
+        contact = splitted;
+        CleanupDeletePushL(contact);
+        while (contact)
+            {
+            // Split as long as must
+            splitted = SplitContactLC(*contact);
+            iNewSimContacts.AppendL(contact);
+            if (splitted)
+                {
+                CleanupStack::Pop(2); // contact, splitted
+                contact = splitted;
+                CleanupDeletePushL(contact);
+                }
+            else
+                {
+                CleanupStack::Pop(); // contact
+                contact = NULL;
+                }
+            }
+        result = ETrue;
+        }
+    // Contact can be empty if the CopyFieldL failed
+    else if ( contact->Fields().FieldCount() > 0 )
+        {
+        // Contact is not splitted
+        iNewSimContacts.AppendL(contact);
+        CleanupStack::Pop(); // contact
+        }
+
+    return result;
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::SplitContactLC
+//
+// Splits the master contact if there is too much fields to one
+// SIM contact.
+// --------------------------------------------------------------------------
+//
+MVPbkStoreContact* CPsu2SimContactProcessor::SplitContactLC( 
+        MVPbkStoreContact& aSimContact )
+    {
+    MVPbkStoreContactFieldCollection& fields = aSimContact.Fields();
+    TInt count =  fields.FieldCount();
+
+    RArray<TFieldTypeCounter> fieldTypeCounterArray;
+    CleanupClosePushL(fieldTypeCounterArray);
+
+    RArray<TInt> removedIndexes;
+    CleanupClosePushL(removedIndexes);
+
+    MVPbkStoreContact* splittedContact = NULL;
+    // Loop all fields of the source contact
+    for (TInt i = 0; i < count; ++i)
+        {
+        const MVPbkFieldType* type =
+            fields.FieldAt(i).BestMatchingFieldType();
+        // Check all the data fields
+        if (type && !iNameFormatter.IsTitleFieldType( *type ) )
+            {
+            TInt maxNumber = MaxNumberOfFieldL( aSimContact, *type );
+            // Compare the max amount fields to the current amount of the
+            // fields in the contact
+            if ( CheckNumberOfFieldsL
+                    ( maxNumber, *type, fieldTypeCounterArray ) )
+                {
+                // Create a new contact for the fields that can not fit to
+                // the source contact
+                if (!splittedContact)
+                    {
+                    splittedContact = iTargetStore.CreateNewContactLC();
+                    AddNameFieldsL(aSimContact, *splittedContact);
+                    }
+                // Copy field to the new contact
+                // and remove it from the source
+                CopyFieldL( fields.FieldAt(i), *splittedContact, *type );
+                removedIndexes.AppendL(i);
+                }
+            }
+        }
+
+    count = removedIndexes.Count();
+    for (TInt k = count - 1; k >= 0; --k)
+        {
+        aSimContact.RemoveField(removedIndexes[k]);
+        }
+
+    if (splittedContact)
+        {
+        CleanupStack::Pop(); // splittedContact
+        CleanupStack::PopAndDestroy(2); // removedIndexes,
+                                        // fieldTypeCounterArray
+        CleanupDeletePushL(splittedContact);
+        }
+    else
+        {
+        CleanupStack::PopAndDestroy(2); // removedIndexes,
+                                        // fieldTypeCounterArray
+        }
+    return splittedContact;
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::CopyFieldL
+//
+// Copies the source field to the target contact, EFalse if not copied.
+// --------------------------------------------------------------------------
+//
+TBool CPsu2SimContactProcessor::CopyFieldL( 
+        const MVPbkBaseContactField& aFieldToCopy,
+        MVPbkStoreContact& aTarget,
+        const MVPbkFieldType& aSimType )
+    {
+    TBool result = ETrue;
+    if ( aFieldToCopy.FieldData().DataType() == EVPbkFieldStorageTypeText )
+        {
+        // Get source data
+        const MVPbkContactFieldTextData& sourceData =
+            MVPbkContactFieldTextData::Cast( aFieldToCopy.FieldData() );
+        // Create target field
+        MVPbkStoreContactField* field = aTarget.CreateFieldLC( aSimType );
+        // Get target data
+        MVPbkContactFieldTextData& targetData =
+            MVPbkContactFieldTextData::Cast( field->FieldData() );
+
+        // Invalid characters must be removed before copying if the
+        // field is number
+        if ( IsNumberType( aSimType ) )
+            {
+            HBufC* number = CreateValidNumberLC( sourceData.Text(),
+                iCopyToSimFieldInfoArray.NumberKeyMap() );
+            result = CopyFieldDataL( *number, targetData, aSimType );
+            CleanupStack::PopAndDestroy( number );
+            }
+        else
+            {
+            result = CopyFieldDataL
+                ( sourceData.Text(), targetData, aSimType );
+            }
+
+        if ( result )
+            {
+            aTarget.AddFieldL(field);
+            CleanupStack::Pop(); // field
+            }
+        else
+            {
+            CleanupStack::PopAndDestroy(); // field
+            }
+        }
+    return result;
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::CopyTitleFieldDataL
+//
+// Copies title field from source contact to given field type.
+// --------------------------------------------------------------------------
+//
+void CPsu2SimContactProcessor::CopyTitleFieldDataL( 
+        MVPbkStoreContact& aSource, MVPbkStoreContact& aTarget,
+        const MVPbkFieldType& aSimTitleFieldType )
+    {
+    HBufC* title = iNameFormatter.GetContactTitleOrNullL
+        ( aSource.Fields(),
+          MPbk2ContactNameFormatter::EPreserveLeadingSpaces );
+
+    if (title)
+        {
+        CleanupStack::PushL( title );
+        MVPbkStoreContactField* field =
+            aTarget.CreateFieldLC( aSimTitleFieldType );
+        MVPbkContactFieldTextData& data =
+            MVPbkContactFieldTextData::Cast( field->FieldData() );
+        TruncateAndCopyFieldDataL( *title, data );
+        aTarget.AddFieldL(field);
+        CleanupStack::Pop(field);
+        CleanupStack::PopAndDestroy(title);
+        }
+    }
+
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::CopyFieldDataL
+// --------------------------------------------------------------------------
+//
+TBool CPsu2SimContactProcessor::CopyFieldDataL( 
+        const TDesC& aSource, MVPbkContactFieldTextData& aTarget,
+        const MVPbkFieldType& aSimType )
+    {
+    TBool result = ETrue;
+    // If truncation is allowed for the field type then truncate
+    // and copy
+    if ( iCopyToSimFieldInfoArray.TruncationAllowed( aSimType ) )
+        {
+        TruncateAndCopyFieldDataL( aSource, aTarget );
+        }
+    // otherwise check if there is enough space for data
+    else if ( aSource.Length() <=
+            aTarget.MaxLength() )
+        {
+        aTarget.SetTextL( aSource );
+        }
+    else
+        {
+        result = EFalse;
+        }
+    return result;
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::TruncateAndCopyFieldDataL
+// --------------------------------------------------------------------------
+//
+void CPsu2SimContactProcessor::TruncateAndCopyFieldDataL( 
+        const TDesC& aSource, 
+        MVPbkContactFieldTextData& aTarget )
+    {
+    PBK2_DEBUG_PRINT( PBK2_DEBUG_STRING
+        ("CPsu2SimContactProcessor::TruncateAndCopyFieldDataL max len: [%i]"),
+        aTarget.MaxLength() );
+
+    // Try first with SMS 7-bit encoding
+    TInt unconvertedCount(0);
+    TPtrC data = iCharConvSms7Bit->CheckFieldLengthL
+            ( aSource, aTarget.MaxLength(), unconvertedCount );
+
+    PBK2_DEBUG_PRINT( PBK2_DEBUG_STRING
+        ("CPsu2SimContactProcessor:: max converted len: [%i], unconv: [%i]"),
+        data.Length(), unconvertedCount );
+
+    // If any characters could not be converted with SMS 7-bit encoding we need
+    // to check length using UCS-2 encoding which requires more space per a
+    // character.
+    // With some character sets that contain less than 128 characters the
+    // resulting data is shorter than the maximum field length allowed by the
+    // (U)SIM. In other words we may truncate the text fields stored to the
+    // (U)SIM more than would be required by the (U)SIM. This is because
+    // there are actually three possible coding schemes for such character
+    // sets available. See Annex B in document 3GPP TS 11.11.
+    // The conversion is made by SIM ATK TSY so we would need to have some
+    // SAT server API available to be able to determine the actual maximum
+    // length for the data unambiguously.
+    if( unconvertedCount > 0 )
+        {
+        // Leave one character extra space in case of non-7-bit conversions
+        // This seems to be a feature of (U)SIM that it requires one extra byte
+        // for unicode contact name (see also the 3GPP reference mentioned in
+        // the comment above).
+        data.Set( iCharConvUcs2->CheckFieldLengthL
+                ( aSource, aTarget.MaxLength() - 1, unconvertedCount ) );
+        PBK2_DEBUG_PRINT( PBK2_DEBUG_STRING
+            ("CPsu2SimContactProcessor:: max converted len: [%i], unconv: [%i]"),
+            data.Length(), unconvertedCount );
+        }
+
+    aTarget.SetTextL( data );
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::MaxNumberOfFieldL
+//
+// Returns the maximum amount of field of given type that
+// can be added to the contact.
+// --------------------------------------------------------------------------
+//
+TInt CPsu2SimContactProcessor::MaxNumberOfFieldL( 
+        MVPbkStoreContact& aContact, const MVPbkFieldType& aType )
+    {
+    TInt maxAmount( aContact.MaxNumberOfFieldL( aType ) );
+    // For numbers it must be checked that is ANR file(s) of USIM full.
+    if ( IsNumberType( aType ) )
+        {
+        // Reduce the max according to amount of ANR errors from TSY
+        maxAmount -= iNumOfAdditionalNumberErrors;
+        }
+    return maxAmount;
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::RemoveContactsThatHaveOnlyNameL
+// --------------------------------------------------------------------------
+//
+void CPsu2SimContactProcessor::RemoveContactsThatHaveOnlyNameL()
+    {
+    const TInt cntCount = iNewSimContacts.Count();
+    for ( TInt i = cntCount - 1; i >= 0; --i )
+        {
+        if ( !IsValidContactToSaveL( *iNewSimContacts[i] ) )
+            {
+            delete iNewSimContacts[i];
+            iNewSimContacts.Remove( i );
+            }
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CPsu2SimContactProcessor::IsValidContactToSaveL
+// --------------------------------------------------------------------------
+//
+TBool CPsu2SimContactProcessor::IsValidContactToSaveL( 
+        MVPbkStoreContact& aSimContact )
+    {
+    // Specification says that "If the contact does not contain any number
+    // it is not copied to SIM.". This is valid for 2G SIMs but it's assumed
+    // that in USIM this means that contact that don't have any data fields
+    // is not copied. In other words contact is valid if it has other than
+    // title fields.
+    MVPbkStoreContactFieldCollection& fields = aSimContact.Fields();
+    const TInt fieldCount = fields.FieldCount();
+    for ( TInt i = 0; i < fieldCount; ++i )
+        {
+        if ( !iNameFormatter.IsTitleField( fields.FieldAt( i ) ) )
+            {
+            return ETrue;
+            }
+        }
+    return EFalse;
+    }
+
+//  End of File