javaextensions/pim/versit/src.s60/cpimcardpropertyconverter.cpp
branchRCL_3
changeset 19 04becd199f91
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javaextensions/pim/versit/src.s60/cpimcardpropertyconverter.cpp	Tue Apr 27 16:30:29 2010 +0300
@@ -0,0 +1,1200 @@
+/*
+* Copyright (c) 2008 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:  Converts properties PIM <-> vCard
+ *
+*/
+
+
+// CLASS HEADER
+#include "cpimcardpropertyconverter.h"
+
+// INTERNAL INCLUDES
+#include "pimcommon.h"
+#include "pimpanics.h"
+#include "pimversit.h"
+#include "cpimitem.h"
+#include "cpimvcardparser.h"
+#include "cpimcontactitem.h"
+#include "cpimparserproperty.h"
+#include "cpimcontactvalidator.h"
+#include "cpimvcardparserparamarray.h"
+#include "logger.h"
+
+// EXTERNAL INCLUDES
+#include <vcard.h>
+#include <cntdef.h> // parameter literals
+#include <tz.h>
+#include <tzconverter.h>
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+CPIMCardPropertyConverter* CPIMCardPropertyConverter::NewL(
+    const CPIMContactValidator& aValidator)
+{
+    JELOG2(EPim);
+    CPIMCardPropertyConverter* self =
+        new(ELeave) CPIMCardPropertyConverter(aValidator);
+
+    return self;
+}
+
+// Destructor
+CPIMCardPropertyConverter::~CPIMCardPropertyConverter()
+{
+    JELOG2(EPim);
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertFieldL
+// Inserts a Field from a PIM Contact Item to CParserVCard as a property.
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertFieldL(const CPIMItem& aItem, // item to read the field from
+        CParserVCard& aParser, // parser to insert the property to.
+        TPIMContactField aField) // field to convert
+{
+    JELOG2(EPim);
+    TInt valueCount = aItem.CountValuesL(aField);
+    for (TInt i = 0; i < valueCount; i++)
+    {
+        switch (aField)
+        {
+        case EPIMContactAddr:
+        {
+            ConvertAddressFieldL(aItem, aParser, aField, i);
+            break;
+        }
+        case EPIMContactBirthday:
+        case EPIMContactRevision:
+        case EPIMContactExtAnniversary:
+        {
+            ConvertDateFieldL(aItem, aParser, aField, i);
+            break;
+        }
+        case EPIMContactEmail:
+        case EPIMContactNote:
+        case EPIMContactTel:
+        case EPIMContactTitle:
+        case EPIMContactUrl:
+        case EPIMContactNickname:
+        case EPIMContactExtSip:
+        case EPIMContactExtDtmf:
+        case EPIMContactExtWvUserId:
+        case EPIMContactExtSpouse:
+        case EPIMContactExtChildren:
+        case EPIMContactExtAssistantName:
+        case EPIMContactExtVoip:
+        case EPIMContactExtPTT:
+        case EPIMContactExtSWIS:
+            // UID is required by VFX. Do not fix even though this is wrong
+        case EPIMContactUid:
+        {
+            ConvertStringFieldL(aItem, aParser, aField, i);
+            break;
+        }
+        case EPIMContactName:
+        {
+            ConvertNameFieldL(aItem, aParser, aField, i);
+            break;
+        }
+        case EPIMContactOrg:
+        case EPIMContactExtDepartment:
+        {
+            ConvertOrganisationalFieldsL(aItem, aParser, aField, i);
+            break;
+        }
+        case EPIMContactPhoto:
+        {
+            ConvertPhotoFieldL(aItem, aParser, aField, i);
+            break;
+        }
+        case EPIMContactClass:
+        {
+            ConvertClassFieldL(aItem, aParser, aField, i);
+            break;
+        }
+        // PIM API vCard parser does not support converting formatted names
+        // since the conversion is not unambiguous and an optional feature
+        // for vCard parsers based on IMC vCard specification. Also, PIM
+        // API internal specification version 4.3 does not allow
+        // Contact.FORMATTED_NAME to be a supported fields in S60 implementations
+        case EPIMContactFormattedName:
+        case EPIMContactFormattedAddr:
+        case EPIMContactPhotoUrl:
+        case EPIMContactPublicKey:
+        case EPIMContactPublicKeyString:
+        default:
+        {
+            __ASSERT_DEBUG(EFalse, User::Panic(KPIMPanicCategory,
+                                               EPIMPanicUnsupportedField));
+            return;
+        }
+        }
+    }
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertPropertyL
+// Inserts a proprety from a vCard to a PIM Contact Item as a field
+// -----------------------------------------------------------------------------
+//
+CParserVCard* CPIMCardPropertyConverter::ConvertPropertyL(
+    const CParserProperty& aProperty, // property to convert
+    CPIMContactItem& aItem) // item to insert the field to
+{
+    JELOG2(EPim);
+    TUid valueTypeUid = aProperty.Uid();
+    TInt valueType = valueTypeUid.iUid;
+    CParserVCard* retVal = NULL;
+
+    // The following, rather ugly, cast makes it possible for us to access
+    // the protected iArrayOfParams field.
+    const CPIMParserProperty& property =
+        static_cast<const CPIMParserProperty&>(aProperty);
+
+    // CLASS field is converted before others
+    if (aProperty.Name().CompareF(KVersitTokenClass) == 0)
+    {
+        ConvertClassPropertyL(property, aItem);
+    }
+    // If SOUND field holds parameter X-IRMC-N, it countains name reading fields
+    else if ((aProperty.Name().CompareF(KVersitTokenSOUND) == 0)
+             && (aProperty.Param(KVersitParam8NamePrn)))
+    {
+        ConvertNameReadingFieldL(property, aItem);
+    }
+    else
+    {
+        switch (valueType)
+        {
+        case KVersitPropertyBinaryUid:
+        {
+            ConvertPhotoPropertyL(property, aItem);
+            break;
+        }
+        case KVersitPropertyCDesCArrayUid:
+        {
+            // name and address arrays
+            ConvertCDesCArrayPropertyL(property, aItem);
+            break;
+        }
+        case KVersitPropertyDateUid:
+        case KVersitPropertyDateTimeUid:
+        {
+            // REV, BDAY, X-ANNIVERSARY
+            ConvertDatePropertyL(property, aItem);
+            break;
+        }
+        case KVersitPropertyHBufCUid:
+        {
+            ConvertStringPropertyL(property, aItem);
+            break;
+        }
+        case KVCardPropertyAgentUid:
+        {
+            retVal = ConvertAgentPropertyL(property);
+        }
+        default:
+        {
+            // don't support, don't care
+        }
+        }
+    }
+    return retVal;
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::CPIMCardPropertyConverter
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+//
+CPIMCardPropertyConverter::CPIMCardPropertyConverter(
+    const CPIMContactValidator& aValidator) :
+        iContactValidator(aValidator)
+{
+    JELOG2(EPim);
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertAgentPropertyL
+// Converts an agent property.
+// -----------------------------------------------------------------------------
+//
+inline CParserVCard* CPIMCardPropertyConverter::ConvertAgentPropertyL(
+    const CPIMParserProperty& aProperty) // property to convert
+{
+    JELOG2(EPim);
+    CParserPropertyValueAgent* propertyValue =
+        static_cast<CParserPropertyValueAgent*>(aProperty.Value());
+    CParserVCard* value = propertyValue->Value();
+    return value;
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::AddParamHomeAndWorkL
+// Adds parameters "HOME" and "WORK" if necessary to given property parameter
+// array.
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::AddParamHomeAndWorkL(TPIMAttribute aAttributes, // checked if it contains AttrHome or AttrWork
+        CPIMVCardParserParamArray& aParamArray) // array to add the parameters to
+{
+    JELOG2(EPim);
+    if ((aAttributes & EPIMContactAttrHome) != 0)
+    {
+        AddParserParameterL(aParamArray, KVersitParam8Home());
+    }
+    if ((aAttributes & EPIMContactAttrWork) != 0)
+    {
+        AddParserParameterL(aParamArray, KVersitParam8Work());
+    }
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertAddressFieldL
+// Converts an address field from a PIM Contact Item to CParserVCard.
+// The field is stored in the parser as a property.
+// The address values are stored in CParserPropertyValue
+// CParserProperty is created, given the value.
+// Parameters are added, and finally the property is added to parser.
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertAddressFieldL(const CPIMItem& aItem, // item to convert from
+        CParserVCard& aParser, // parsers to write to
+        TPIMContactField aField, // field to convert
+        TInt aIndex) // index to the field
+{
+    JELOG2(EPim);
+    // Add attributes if there are any
+    TPIMAttribute attributes = aItem.getAttributes(aField, aIndex);
+    CPIMVCardParserParamArray* paramArray =
+        new(ELeave) CPIMVCardParserParamArray(3);
+    CleanupStack::PushL(paramArray);
+    AddParamHomeAndWorkL(attributes, *paramArray);
+
+    const CDesCArray& itemAddrArray = aItem.GetStringArrayL(aField, aIndex);
+    // Copy the array
+    TInt elementCount = itemAddrArray.Count();
+    CDesCArrayFlat* addrArray = new(ELeave) CDesCArrayFlat(elementCount);
+    CleanupStack::PushL(addrArray);
+    for (TInt i = 0; i < elementCount; i++)
+    {
+        if (itemAddrArray[i].Compare(KPIMNullArrayElement) == 0)
+        {
+            addrArray->AppendL(KNullDesC);
+        }
+        else
+        {
+            addrArray->AppendL(itemAddrArray[i]);
+        }
+    }
+
+    CParserPropertyValue* propertyValue =
+        new(ELeave) CParserPropertyValueCDesCArray(addrArray);
+    // addrArray is now owned by propertyValue
+    CleanupStack::Pop(addrArray);
+    AddPropertyToParserL(propertyValue, KVersitTokenADR(), paramArray, aParser);
+    // The paramArray and propertyValue are popped by the previous function call
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertNameFieldL
+// Converts a Name field from a PIM Contact Item to CParserVCard.
+// The field is stored in the parser as a property.
+// The name values are stored in CParserPropertyValue
+// CParserProperty is created, given the value.
+// Parameters are added, and finally the property is added to parser.
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertNameFieldL(const CPIMItem& aItem, // item to convert from
+        CParserVCard& aParser, // parsers to write to
+        TPIMContactField aField, // field to convert
+        TInt aIndex) // index to the field
+{
+    JELOG2(EPim);
+    TBool readingFieldsPresent = EFalse;
+    const CDesCArray& itemNameArray = aItem.GetStringArrayL(aField, aIndex);
+    TInt elementCount = itemNameArray.Count();
+    CDesCArray* readingArray = new(ELeave) CDesCArrayFlat(2);
+    CleanupStack::PushL(readingArray);
+    CDesCArray* nameArray = new(ELeave) CDesCArrayFlat(elementCount);
+    CleanupStack::PushL(nameArray);
+
+    // Names should be ordered as follows:
+    // Family name, Given name, Middle name, Prefix, Suffix
+    // so check TPIMContactNameElement that it is in the right order
+    for (TInt i = 0; i < elementCount; i++)
+    {
+        // Skip extended name reading fields on here
+        if (i == EPIMContactExtFamilyNameReading || i
+                == EPIMContactExtGivenNameReading)
+        {
+            if (itemNameArray[i].Compare(KPIMNullArrayElement) != 0)
+            {
+                readingFieldsPresent = ETrue;
+                readingArray->AppendL(itemNameArray[TPIMContactNameElement(i)]);
+            }
+            else
+            {
+                readingArray->AppendL(KNullDesC);
+            }
+        }
+        // Replace null array elements with KNullDesC
+        else if (itemNameArray[i].Compare(KPIMNullArrayElement) == 0)
+        {
+            nameArray->AppendL(KNullDesC);
+        }
+        else
+        {
+            nameArray->AppendL(itemNameArray[TPIMContactNameElement(i)]);
+        }
+    }
+    CParserPropertyValue* propertyValue =
+        new(ELeave) CParserPropertyValueCDesCArray(nameArray);
+    // nameArray is now owned by propertyValue
+    CleanupStack::Pop(nameArray);
+    AddPropertyToParserL(propertyValue, KVersitTokenN(), NULL, aParser);
+    // The paramArray and propertyValue are popped by the previous function call
+
+    // Add name reading fields if they are present
+    if (readingFieldsPresent)
+    {
+        // Create parameter array
+        CPIMVCardParserParamArray* paramArray =
+            new(ELeave) CPIMVCardParserParamArray(1);
+        CleanupStack::PushL(paramArray);
+        // Create pronunciation parameter and add it to the parameter array
+        AddParserParameterL(*paramArray, KVersitParam8NamePrn());
+        // Create a new proprety value from the reading names array
+        CParserPropertyValue* readingPropValue =
+            new(ELeave) CParserPropertyValueCDesCArray(readingArray);
+        // nameArray is now owned by propertyValue
+        CleanupStack::Pop(paramArray);
+        CleanupStack::Pop(readingArray);
+        CleanupStack::PushL(paramArray);
+        AddPropertyToParserL(readingPropValue, KVersitTokenSOUND(), paramArray,
+                             aParser);
+        // The paramArray and propertyValue are popped
+        // by the previous function call
+    }
+    else
+    {
+        // Clean the reading array
+        CleanupStack::PopAndDestroy(readingArray);
+    }
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertCDesCArrayPropertyL
+// Converts name and address arrays from a vCard to a PIM Contact Item
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertCDesCArrayPropertyL(
+    const CPIMParserProperty& aProperty, // property to convert
+    CPIMContactItem& aItem) // item to insert the field to
+{
+    JELOG2(EPim);
+    TPIMField field = aProperty.MatchContactField();
+
+    if ((field != EPIMContactAddr) && (field != EPIMContactName) && (field
+            != EPIMContactOrg) && (field != EPIMContactExtChildren))
+    {
+        // unsupported properties are silently discarded
+        return;
+    }
+
+    CParserPropertyValueCDesCArray* propertyValue =
+        static_cast<CParserPropertyValueCDesCArray*>(aProperty.Value());
+    const CDesCArray* value = propertyValue->Value();
+
+    // In ORG field, the Organizational name is the first item and the second
+    // item is extended department name
+    if (field == EPIMContactOrg)
+    {
+        TInt valueCount = value->Count();
+        if (valueCount >= 1)
+        {
+            HBufC* orgValue = value->MdcaPoint(0).AllocLC();
+            aItem.AddStringL(field, KPIMAttrNone, orgValue);
+            CleanupStack::Pop(orgValue);
+            // Get department from the array
+            if (valueCount > 1)
+            {
+                HBufC* department = value->MdcaPoint(1).AllocLC();
+                aItem.AddStringL(EPIMContactExtDepartment, KPIMAttrNone,
+                                 department);
+                CleanupStack::Pop(department);
+            }
+        }
+        return;
+    }
+    // Children is imported as a CDesCArray so we have to convert it here
+    if (field == EPIMContactExtChildren && value->Count() >= 1)
+    {
+        // We are only interested about the first value
+        HBufC* children = value->MdcaPoint(0).AllocLC();
+        aItem.AddStringL(field, KPIMAttrNone, children);
+        CleanupStack::Pop(children);
+        return;
+    }
+
+    // Copy the array
+    TInt elementCount = value->Count();
+    CDesCArrayFlat* itemArray = new(ELeave) CDesCArrayFlat(elementCount);
+    CleanupStack::PushL(itemArray);
+    TInt i;
+    for (i = 0; i < elementCount; i++)
+    {
+        itemArray->AppendL((*value)[i]);
+    }
+
+    // If we need more elements, add nulls
+    TInt itemElementCount = iContactValidator.NumElementsL(field);
+    TPtrC nullArrayElement(KPIMNullArrayElement);
+    for (; i < itemElementCount; i++)
+    {
+        itemArray->AppendL(nullArrayElement);
+    }
+
+    TPIMAttribute attributes = KPIMAttrNone;
+    // Usually people only have one name. However, both PIM api and
+    // vCards allow two names: Formatted name is not supported anymore since
+    // converting it is not unambiguous and it must not be supported by the
+    // PIM API internal specification version 4.3. IMC's vCard specification
+    // states that Formatted name is an optional field for vCard parsers.
+    if (field == EPIMContactName && aItem.CountValuesL(EPIMContactName) > 0)
+    {
+        // we already have a name, so we just change the current one
+        // NOTE: There can be name reading fields before the name array
+        // elements are added so we have take care of it
+        const CDesCArray& nameArray = aItem.GetStringArrayL(EPIMContactName, 0);
+        TPtrC name(nameArray.MdcaPoint(EPIMContactExtFamilyNameReading));
+        if (name != KPIMNullArrayElement)
+        {
+            // Delete empty item and add existing item
+            itemArray->Delete(EPIMContactExtFamilyNameReading);
+            itemArray->InsertL(EPIMContactExtFamilyNameReading, name);
+        }
+        name.Set(nameArray.MdcaPoint(EPIMContactExtGivenNameReading));
+        if (name != KPIMNullArrayElement)
+        {
+            // Delete empty item and add existing item
+            itemArray->Delete(EPIMContactExtGivenNameReading);
+            itemArray->InsertL(EPIMContactExtGivenNameReading, name);
+        }
+
+        aItem.SetStringArrayL(EPIMContactName, 0, attributes, itemArray);
+        CleanupStack::Pop(); // itemArray now owned by aItem
+        return;
+    }
+    else
+    {
+        // address can have attributes
+        attributes = aProperty.MatchHomeAndWorkAttributes();
+    }
+    aItem.AddStringArrayL(field, attributes, itemArray);
+    CleanupStack::Pop(); // itemArray now owned by aItem
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertDateFieldL
+// Converts a date field from a PIM Contact Item to a CParserVCard.
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertDateFieldL(const CPIMItem& aItem, // item to convert from
+        CParserVCard& aParser, // parser to insert the property to.
+        TPIMContactField aField, // field to convert
+        TInt aIndex) // index to the field
+{
+    JELOG2(EPim);
+    TVersitDateTime* versitDateTime = NULL;
+    CParserPropertyValue* propertyValue = NULL;
+    TPtrC8 propertyName(KNullDesC8);
+
+    // Handle fields separately
+    switch (aField)
+    {
+    case EPIMContactExtAnniversary:
+    case EPIMContactBirthday:
+    {
+        const TPIMDate date = aItem.GetDateL(aField, aIndex);
+        // Convert times to local time since UTC time designator is not
+        // supported in anniversary and birthday fields
+        RTz tzServer;
+        User::LeaveIfError(tzServer.Connect());
+        CleanupClosePushL(tzServer);
+
+        // Create timezone converter
+        CTzConverter* converter = CTzConverter::NewL(tzServer);
+        CleanupStack::PushL(converter);
+
+        TTime localTime(date);
+        User::LeaveIfError(converter->ConvertToLocalTime(localTime));
+        CleanupStack::PopAndDestroy(2); // converter, tzServer
+
+        versitDateTime
+        = new(ELeave) TVersitDateTime(date.DateTime(), TVersitDateTime::EIsMachineLocal);
+
+        // Set specific property name
+        if (aField == EPIMContactBirthday)
+        {
+            propertyName.Set(KVersitTokenBDAY);
+        }
+        else if (aField == EPIMContactExtAnniversary)
+        {
+            propertyName.Set(KVersitTokenAnniversary);
+        }
+        CleanupDeletePushL(versitDateTime);
+        // Create new date property value
+        propertyValue = new(ELeave) CParserPropertyValueDate(versitDateTime);
+        break;
+    }
+    case EPIMContactRevision:
+    {
+        TPIMDate date = aItem.LastModified();
+        versitDateTime
+        = new(ELeave) TVersitDateTime(date.DateTime(), TVersitDateTime::EIsUTC);
+        propertyName.Set(KVersitTokenREV);
+        CleanupDeletePushL(versitDateTime);
+        // Create new date and time property value
+        propertyValue
+        = new(ELeave) CParserPropertyValueDateTime(versitDateTime);
+        break;
+    }
+    default:
+    {
+        User::Panic(KPIMPanicCategory, EPIMPanicUnsupportedDateField);
+        break;
+    }
+    }
+    // versitDateTime is now owned by propertyValue
+    CleanupStack::Pop(versitDateTime);
+    AddPropertyToParserL(propertyValue, propertyName, NULL, aParser);
+    // The paramArray and propertyValue are popped by the previous function call
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertDatePropertyL
+// Converts a birthday from a vCard to a PIM Contact Item
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertDatePropertyL(
+    const CPIMParserProperty& aProperty, // property to convert
+    CPIMContactItem& aItem) // item to insert the field to
+{
+    JELOG2(EPim);
+    TPIMField field = aProperty.MatchContactField();
+    switch (field)
+    {
+    case EPIMContactExtAnniversary:
+    case EPIMContactBirthday:
+    case EPIMContactRevision:
+    {
+        // All date property values are equal (in some way)
+        CParserPropertyValueDateTime* value =
+            static_cast<CParserPropertyValueDateTime*>(aProperty.Value());
+        // Get versit date time object. Both date time properties
+        // return TVersitDateTime so we don't care about the actual object
+        const TVersitDateTime* vDateTime = value->Value();
+        // Add new date to PIM item
+        TPIMDate date(vDateTime->iDateTime);
+        aItem.AddDateL(field, KPIMAttrNone, date);
+        break;
+    }
+    default:
+    {
+        // Discard other fields
+    }
+    }
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertStringFieldL
+// Converts a string field from a PIM Item to vCard
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertStringFieldL(const CPIMItem& aItem, // item to convert from
+        CParserVCard& aParser, // parser to write to
+        TPIMContactField aField, // field to convert
+        TInt aIndex) // index to the value in the field
+{
+    JELOG2(EPim);
+    // We'll gather all property parameters (attributes) to an array
+    // which we give to the property in construction. It seems to be
+    // the only way to give a property multiple parameters of same type.
+    TPIMAttribute attributes = aItem.getAttributes(aField, aIndex);
+    // Assistant tel field is mapped for its own field in s60, so
+    // it must not be associated with the standard TEL field in vCards
+    if (aField == EPIMContactTel && (attributes & EPIMContactAttrAsst) != 0)
+    {
+        const TDesC& asstTel = aItem.GetStringL(aField, aIndex);
+        CParserPropertyValue* propertyValue = CParserPropertyValueHBufC::NewL(
+                                                  asstTel);
+        AddPropertyToParserL(propertyValue, KPIMVersitTokenASSTTEL(), NULL,
+                             aParser);
+        // Needed cleanup stack cleanup is done within the function
+        return;
+    }
+    CPIMVCardParserParamArray* paramArray =
+        new(ELeave) CPIMVCardParserParamArray(3);
+    CleanupStack::PushL(paramArray);
+
+    TPtrC8 propertyName; // mapped from field identifier
+
+    switch (aField)
+    {
+    case EPIMContactEmail:
+    {
+        propertyName.Set(KVersitTokenEMAIL);
+        AddParamHomeAndWorkL(attributes, *paramArray);
+        // Contacts Model and PIM support PREF attribute, how ever
+        // vCard specification does not. Thus we don't add it.
+        break;
+    }
+    case EPIMContactNote:
+    {
+        propertyName.Set(KVersitTokenNOTE);
+        break;
+    }
+    case EPIMContactTel:
+    {
+        propertyName.Set(KVersitTokenTEL);
+        if ((attributes & EPIMContactAttrFax) != 0)
+        {
+            AddParserParameterL(*paramArray, KVersitParam8Fax());
+        }
+        if ((attributes & EPIMContactAttrMobile) != 0)
+        {
+            AddParserParameterL(*paramArray, KVersitParam8Cell());
+        }
+        if ((attributes & EPIMContactAttrPager) != 0)
+        {
+            AddParserParameterL(*paramArray, KVersitParam8Pager());
+        }
+        if ((attributes & (EPIMContactAttrPager | EPIMContactAttrFax
+                           | EPIMContactAttrSms | EPIMContactAttrAuto
+                           | EPIMContactAttrExtVideoCall)) == 0)
+        {
+            AddParserParameterL(*paramArray, KVersitParam8Voice());
+        }
+        if ((attributes & (EPIMContactAttrPreferred)) != 0)
+        {
+            AddParserParameterL(*paramArray, KVersitParam8Pref());
+        }
+        if ((attributes & EPIMContactAttrAuto) != 0)
+        {
+            AddParserParameterL(*paramArray, KVersitParam8Car());
+        }
+        if ((attributes & EPIMContactAttrExtVideoCall) != 0)
+        {
+            AddParserParameterL(*paramArray, KVersitParam8Video());
+        }
+        // Contacts Model and PIM support SMS attribute, how ever
+        // vCard specification does not. Thus we ignore it.
+        // ATTR_OTHER is also ingored because Contacts Model doesn't
+        // provide anything where we could map ATTR_OTHER
+
+        // Add HOME or WORK after all other attributes
+        AddParamHomeAndWorkL(attributes, *paramArray);
+        break;
+    }
+    case EPIMContactTitle:
+    {
+        propertyName.Set(KVersitTokenTITLE);
+        break;
+    }
+    case EPIMContactUrl:
+    {
+        propertyName.Set(KVersitTokenURL);
+        AddParamHomeAndWorkL(attributes, *paramArray);
+        break;
+    }
+    case EPIMContactNickname:
+    {
+        propertyName.Set(KPIMVersitTokenNICKNAME);
+        break;
+    }
+    case EPIMContactExtDtmf:
+    {
+        propertyName.Set(KPIMVersitTokenDTMF);
+        break;
+    }
+    case EPIMContactExtWvUserId:
+    {
+        propertyName.Set(KPIMVersitTokenWVID);
+        break;
+    }
+    case EPIMContactExtSpouse:
+    {
+        propertyName.Set(KVersitTokenSpouse);
+        break;
+    }
+    case EPIMContactExtChildren:
+    {
+        propertyName.Set(KVersitTokenChildren);
+        break;
+    }
+    case EPIMContactExtAssistantName:
+    {
+        propertyName.Set(KVersitTokenAssistant);
+        break;
+    }
+    case EPIMContactExtVoip:
+    case EPIMContactExtSip:
+    case EPIMContactExtPTT:
+    case EPIMContactExtSWIS:
+    {
+        TPtrC8 paramName;
+        propertyName.Set(KPIMVersitTokenSIP);
+        // VOIP is mapped to a subfield of X-SIP
+        if (aField == EPIMContactExtVoip)
+        {
+            paramName.Set(KPIMVersitTokenVoip);
+        }
+        // PTT is mapped to a subfield of X-SIP
+        else if (aField == EPIMContactExtPTT)
+        {
+            paramName.Set(KPIMVersitTokenPoc);
+        }
+        // SWIS is mapped to a subfield of X-SIP
+        else if (aField == EPIMContactExtSWIS)
+        {
+            paramName.Set(KPIMVersitTokenSwis);
+        }
+        if (aField != EPIMContactExtSip)
+        {
+            AddParserParameterL(*paramArray, paramName);
+        }
+
+        AddParamHomeAndWorkL(attributes, *paramArray);
+        // Contacts Model and PIM support PREF attribute, how ever
+        // vCard specification does not. Thus we don't add it.
+        break;
+    }
+    case EPIMContactUid:
+    {
+        propertyName.Set(KVersitTokenUID);
+        break;
+    }
+    default:
+    {
+        User::Panic(KPIMPanicCategory, EPIMPanicUnsupportedStringField);
+    }
+    }
+
+    const TDesC& pimStringValue = aItem.GetStringL(aField, aIndex);
+
+    CParserPropertyValue* propertyValue = CParserPropertyValueHBufC::NewL(
+                                              pimStringValue);
+    AddPropertyToParserL(propertyValue, propertyName, paramArray, aParser);
+    // The paramArray and propertyValue are popped by the previous function call
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::AddParserParameterL
+// Adds new parser parameter to the parameter array
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::AddParserParameterL(
+    CPIMVCardParserParamArray& aArrayOfParams, const TPtrC8 aParamName)
+{
+    JELOG2(EPim);
+    // Create a new parameter from the name
+    CParserParam* newParam = CParserParam::NewL(aParamName, KNullDesC8);
+    CleanupStack::PushL(newParam);
+    // Add parameter to the param array
+    aArrayOfParams.AppendL(newParam);
+    // newParam is now owned by the parameter array
+    CleanupStack::Pop(newParam);
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertStringPropertyL
+// Converts a string property from a vCard to a PIM Contact Item
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertStringPropertyL(
+    const CPIMParserProperty& aProperty, // property to convert
+    CPIMContactItem& aItem) // item to insert the field to
+{
+    JELOG2(EPim);
+    TPIMField field = aProperty.MatchContactField();
+    TPIMAttribute attributes = KPIMAttrNone;
+
+    if (field == KErrNotFound)
+    {
+        return;
+    }
+
+    CParserPropertyValueHBufC* propertyValue =
+        static_cast<CParserPropertyValueHBufC*>(aProperty.Value());
+    HBufC* value = propertyValue->TakeValueOwnership();
+    CleanupStack::PushL(value);
+
+    switch (field)
+    {
+    case EPIMContactUrl:
+    case EPIMContactEmail:
+        // vCard EMAIL does not support PREF
+    {
+        attributes = aProperty.MatchHomeAndWorkAttributes();
+        break;
+    }
+    case EPIMContactTel:
+    {
+        // Assistant phone is not an attribute in TEL field
+        // it must be handled separately
+        if (aProperty.Name().CompareF(KPIMVersitTokenASSTTEL) == 0)
+        {
+            attributes = EPIMContactAttrAsst;
+        }
+        else
+        {
+            attributes = aProperty.MatchAllAttributes();
+        }
+        break;
+    }
+    case EPIMContactExtVoip:
+    {
+        // VOIP field can have parameters HOME or WORK
+        attributes = aProperty.MatchHomeAndWorkAttributes();
+        break;
+    }
+    default:
+    {
+        // no attributes
+    }
+    }
+    aItem.AddStringL(field, attributes, value);
+    CleanupStack::Pop(value);
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertPhotoFieldL
+// Converts a photo field from a PIM Contact Item to a vCard
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertPhotoFieldL(const CPIMItem& aItem, // item to convert from
+        CParserVCard& aParser, // parser to write to
+        TPIMContactField aField, // field to convert
+        TInt aIndex) // index to the field
+{
+    JELOG2(EPim);
+    const CPIMByteArray& byteArray = aItem.GetBinaryRawL(aField, aIndex);
+
+    const TUint8& byteRef = byteArray.At(0);
+    const TUint8* bytePtr = &byteRef;
+    TPtrC8 data(bytePtr, byteArray.Count());
+
+    CParserPropertyValue* propertyValue =
+        CParserPropertyValueBinary::NewL(data);
+    AddPropertyToParserL(propertyValue, KVersitTokenPHOTO(), NULL, aParser);
+    // The paramArray and propertyValue are popped by the previous function call
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertPhotoPropertyL
+// Converts a photo field from a vCard to a PIM Contact Item
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertPhotoPropertyL(
+    const CPIMParserProperty& aProperty, // property to convert
+    CPIMContactItem& aItem) // item to insert the field to
+{
+    JELOG2(EPim);
+    if (aProperty.Name().CompareF(KVersitTokenPHOTO) != 0)
+    {
+        // Unsupported binary property, silently discarded
+        return;
+    }
+
+    CParserPropertyValueBinary* propertyValue =
+        static_cast<CParserPropertyValueBinary*>(aProperty.Value());
+    const CBufSeg* value = propertyValue->Value();
+
+    TInt binarySize = value->Size();
+    CPIMByteArray* byteArray = new(ELeave) CPIMByteArray(binarySize);
+    CleanupStack::PushL(byteArray);
+    byteArray->ResizeL(binarySize);
+    TUint8& byteRef = byteArray->At(0);
+    TUint8* bytePtr = &byteRef;
+
+    value->Read(0, static_cast<TAny*>(bytePtr), binarySize);
+
+    aItem.AddBinaryRawL(EPIMContactPhoto, KPIMAttrNone, byteArray);
+    CleanupStack::Pop(byteArray); // byteArray now owned by aItem
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertClassFieldL
+// Converts a CLASS field from a PIM Contact Item to a vCard
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertClassFieldL(const CPIMItem& aItem,
+        CParserVCard& aParser, TPIMContactField aField, TInt aIndex)
+{
+    JELOG2(EPim);
+    TInt classIntValue = aItem.getInt(aField, aIndex);
+    TPtrC classStringValue(KPIMClassStringConfidential());
+    switch (classIntValue)
+    {
+    case EPIMContactClassConfidential:
+    {
+        classStringValue.Set(KPIMClassStringConfidential());
+        break;
+    }
+    case EPIMContactClassPrivate:
+    {
+        classStringValue.Set(KPIMClassStringPrivate());
+        break;
+    }
+    case EPIMContactClassPublic:
+    {
+        classStringValue.Set(KPIMClassStringPublic());
+        break;
+    }
+    default:
+    {
+        __ASSERT_DEBUG(EFalse, User::Invariant());
+        break;
+    }
+    }
+    CParserPropertyValue* propertyValue = CParserPropertyValueHBufC::NewL(
+                                              classStringValue);
+    AddPropertyToParserL(propertyValue, KVersitTokenClass(), NULL, aParser);
+    // The paramArray and propertyValue are popped by the previous function call
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertClassPropertyL
+// Converts CLASS property to a PIM Event item.
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertClassPropertyL(
+    const CPIMParserProperty& aProperty, CPIMContactItem& aItem)
+{
+    JELOG2(EPim);
+    CParserPropertyValueHBufC* propertyValue =
+        static_cast<CParserPropertyValueHBufC*>(aProperty.Value());
+
+    const TPtrC classStringPtr = propertyValue->Value();
+    TInt classInt = KErrCorrupt;
+
+    if (classStringPtr.CompareF(KPIMClassStringPublic) == 0)
+    {
+        classInt = EPIMContactClassPublic;
+    }
+    else if (classStringPtr.CompareF(KPIMClassStringPrivate) == 0)
+    {
+        classInt = EPIMContactClassPrivate;
+    }
+    else if (classStringPtr.CompareF(KPIMClassStringConfidential) == 0)
+    {
+        classInt = EPIMContactClassConfidential;
+    }
+    // else the class value in the originating vCalendar is flawed - ignore
+
+    if (classInt != KErrCorrupt)
+    {
+        aItem.addInt(EPIMContactClass, KPIMAttrNone, classInt);
+    }
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertOrganisationalFieldsL
+// Converts organisational fields from PIM item to CParserVCard
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertOrganisationalFieldsL(
+    const CPIMItem& aItem, CParserVCard& aParser, TPIMContactField /*aField*/,
+    TInt /*aIndex*/)
+{
+    JELOG2(EPim);
+    // Check that the field is not converted twice (e.g one of the
+    // organisational fields have been already converted and caused
+    // other field to be converted also
+    CArrayPtr<CParserProperty>* props = aParser.ArrayOfProperties(EFalse);
+    if (props)
+    {
+        TInt propCount = props->Count();
+        for (TInt i = 0; i < propCount; i++)
+        {
+            const CParserProperty* property = props->At(i);
+            if (property->Name().CompareF(KVersitTokenORG) == 0)
+            {
+                return;
+            }
+        }
+    }
+
+    // Create array from organisation and department fields
+    CDesCArray* orgArray = new(ELeave) CDesCArrayFlat(2);
+    CleanupStack::PushL(orgArray);
+    if (aItem.CountValuesL(EPIMContactOrg) > 0)
+    {
+        const TDesC& org = aItem.GetStringL(EPIMContactOrg, 0);
+        orgArray->AppendL(org);
+    }
+    else
+    {
+        orgArray->AppendL(KNullDesC);
+    }
+    // Department field is after organisational unit
+    if (aItem.CountValuesL(EPIMContactExtDepartment) > 0)
+    {
+        const TDesC& dep = aItem.GetStringL(EPIMContactExtDepartment, 0);
+        orgArray->AppendL(dep);
+    }
+    else
+    {
+        orgArray->AppendL(KNullDesC);
+    }
+
+    CParserPropertyValue* propertyValue =
+        new(ELeave) CParserPropertyValueCDesCArray(orgArray);
+    CleanupStack::Pop(orgArray); // orgArray now owned by propertyValue
+    AddPropertyToParserL(propertyValue, KVersitTokenORG(), NULL, aParser);
+    // The paramArray and propertyValue are popped by the previous function call
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::ConvertNameReadingFieldL
+// Converts name reading fields from vCard to PIM item
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::ConvertNameReadingFieldL(
+    const CPIMParserProperty& aProperty, CPIMContactItem& aItem)
+{
+    JELOG2(EPim);
+    TInt nameCount = iContactValidator.NumElementsL(EPIMContactName);
+
+    // Name reading fields come in a HBufC property value although they
+    // are in an array property value and SHOULD come in as an array.
+    // So, we have to generate an array property from the HBufC proprety
+    CParserPropertyValueHBufC* propertyValue =
+        static_cast<CParserPropertyValueHBufC*>(aProperty.Value());
+    HBufC* value = propertyValue->TakeValueOwnership();
+    CleanupStack::PushL(value);
+    // PIM parser to access protected methods
+    CPIMVCardParser* parser = CPIMVCardParser::NewLC();
+    CDesCArray* readingValues = parser->MakePropertyValueCDesCArrayL(
+                                    value->Des());
+    CleanupStack::PopAndDestroy(2, value); // parser
+    CleanupStack::PushL(readingValues);
+
+    // Array for the new name elements
+    CDesCArray* newNameArray = new(ELeave) CDesCArrayFlat(nameCount);
+    CleanupStack::PushL(newNameArray);
+    // Old elements exist
+    if (aItem.CountValuesL(EPIMContactName) > 0)
+    {
+        const CDesCArray& nameArray = aItem.GetStringArrayL(EPIMContactName, 0);
+        for (TInt i = 0; i < nameCount; i++)
+        {
+            TPtrC element(nameArray.MdcaPoint(i));
+            newNameArray->AppendL(element);
+        }
+        // Add array elements. If the array count is larger than zero
+        // it indicates that at least the Last name reading field is present
+        if (readingValues->Count() > 0)
+        {
+            // Remove old value and add new
+            newNameArray->Delete(EPIMContactExtFamilyNameReading);
+            newNameArray->InsertL(EPIMContactExtFamilyNameReading,
+                                  readingValues->MdcaPoint(0));
+        }
+        // If the array count is larger than one it indicates that
+        // the First name reading is also present
+        if (readingValues->Count() > 1)
+        {
+            newNameArray->Delete(EPIMContactExtGivenNameReading);
+            newNameArray->InsertL(EPIMContactExtGivenNameReading,
+                                  readingValues->MdcaPoint(1));
+        }
+
+        // Set new string array for the contact name field
+        aItem.SetStringArrayL(EPIMContactName, 0, KPIMAttrNone, newNameArray);
+    }
+    // No old name array
+    else
+    {
+        // Create a new array and append name reading values to it
+        for (TInt i = 0; i < nameCount; i++)
+        {
+            newNameArray->AppendL(KPIMNullArrayElement);
+        }
+        // Add array elements. If the array count is larger than zero
+        // it indicates that at least the Last name reading field is present
+        if (readingValues->Count() > 0)
+        {
+            // Remove old value and add new
+            newNameArray->Delete(EPIMContactExtFamilyNameReading);
+            newNameArray->InsertL(EPIMContactExtFamilyNameReading,
+                                  readingValues->MdcaPoint(0));
+        }
+        // If the array count is larger than one it indicates that
+        // the First name reading is also present
+        if (readingValues->Count() > 1)
+        {
+            newNameArray->Delete(EPIMContactExtGivenNameReading);
+            newNameArray->InsertL(EPIMContactExtGivenNameReading,
+                                  readingValues->MdcaPoint(1));
+        }
+        // Add new string array for the cotnact name field
+        aItem.AddStringArrayL(EPIMContactName, KPIMAttrNone, newNameArray);
+    }
+
+    CleanupStack::Pop(newNameArray);
+    CleanupStack::PopAndDestroy(readingValues);
+}
+
+// -----------------------------------------------------------------------------
+// CPIMCardPropertyConverter::AddPropertyToParserL
+// adds a new property value to the parser. NOTE that the property value
+// -----------------------------------------------------------------------------
+//
+void CPIMCardPropertyConverter::AddPropertyToParserL(
+    CParserPropertyValue* aPropertyValue, const TDesC8& aPropertyName,
+    CPIMVCardParserParamArray* aArrayOfParams, CParserVCard& aParser)
+{
+    JELOG2(EPim);
+    // Add property value to the cleanup stack. It should not be added
+    // before this call. This function is an exception and reduces much code
+    CleanupStack::PushL(aPropertyValue);
+    // Create a new parser property from the property value, its name and
+    // array of parameters. If there are no parameters, the aArrayOfParams
+    // should be NULL and the parser property will be generated without params
+    CParserProperty* property = CParserProperty::NewL(*aPropertyValue,
+                                aPropertyName, aArrayOfParams);
+    // property takes ownership of the property value
+    CleanupStack::Pop(aPropertyValue);
+    // Pop aArrayOfParams if it is not NULL
+    if (aArrayOfParams)
+    {
+        CleanupStack::Pop(aArrayOfParams);
+    }
+    // NOTE: property MUST not be pushed to the cleanup stack since the
+    // AddPropertyL pushes it right away to the cleanup stack. So, we must not
+    // push it here to avoid duplicate stack pointers
+    aParser.AddPropertyL(property);
+    // Property is now owned by the parser but we do not have to pop it
+}
+
+// End of file