/*
* 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:  Handles conversion between PIM API Contact Items and Contacts
 *                Model Contact Cards
 *
*/


// CLASS HEADER
#include "cpimcontactitemadapter.h"

// INTERNAL INCLUDES
#include "cpimcontactcategorymanager.h"
#include "pimpanics.h"
#include "mpimcontactitem.h"
#include "mpimitemdata.h"
#include "cpimimagescaler.h"
#include "cpimcontactfieldinfo.h"
#include "logger.h"

// EXTERNAL INCLUDES
#include <cntitem.h> // CContactCard etc.
#include <cntfldst.h> // CContactTextField etc.
#include <tz.h>
#include <tzconverter.h>



// UNNAMED LOCAL NAMESPACE
namespace
{
// Phonebook maps Syncronisation field values with specified
// hard-coded string values. At the time implementing, the header
// was not exported so it is necessary to define own constants
// with same values
_LIT(KPIMContactClassPrivate, "private");
_LIT(KPIMContactClassPublic, "public");
_LIT(KPIMContactClassConfidential, "none");
}

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CPIMContactItemAdapter* CPIMContactItemAdapter::NewL(
    CPIMContactCategoryManager& aCategoryManager)
{
    JELOG2(EPim);
    CPIMContactItemAdapter* self =
        new(ELeave) CPIMContactItemAdapter(aCategoryManager);

    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);

    return self;
}

// destructor
CPIMContactItemAdapter::~CPIMContactItemAdapter()
{
    JELOG2(EPim);
    delete iImageScaler;
    delete iFieldInfo;
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::CardL
// Converts from a PIM item data to a Contacts Model contact card.
// Returns: A contact card with the same information as given in
//          the parameter item. The ownership of the card is
//          transferred to the caller.
// -----------------------------------------------------------------------------
//
CContactCard* CPIMContactItemAdapter::CardL(const MPIMContactItem& aItem)
{
    JELOG2(EPim);
    CContactCard* card = CContactCard::NewLC();
    const MPIMItemData& item = aItem.ItemData();
    __ASSERT_DEBUG((aItem.ContactItemIdL() == 0), User::Panic(
                       KPIMPanicCategory, EPIMPanicInvalidItemID));
    CArrayFix<TPIMField>* fields = item.FieldsLC();

    TInt fieldCount = fields->Count();
    for (TInt i = 0; i < fieldCount; i++)
    {
        ConvertFieldL(aItem, *card,
                      static_cast<TPIMContactField>(fields->At(i)));
    }

    // Set default values if not present in PIM item
    if (item.CountValues(EPIMContactClass) == 0)
    {
        CContentType* type = CContentType::NewL(KUidContactFieldClass,
                                                KUidContactFieldVCardMapClass);
        CleanupStack::PushL(type);

        // Add value to the contact field
        CContactItemField* field = CContactItemField::NewLC(KStorageTypeText,
                                   *type);
        field->SetUserFlags(EContactCategoryNone);
        CContactTextField* textField = field->TextStorage();
        textField->SetTextL(KPIMContactClassPrivate());
        card->AddFieldL(*field);

        CleanupStack::Pop(field);
        CleanupStack::PopAndDestroy(type);
    }

    CleanupStack::PopAndDestroy(fields);
    CleanupStack::Pop(card);
    return card;
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::FillItemL
// Converts from a Contacts Model contact card to the PIM representation.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::FillItemL(MPIMContactItem& aItem, // Item data container to fill with the data
                                       const CContactCard& aCard) // Contact Card to convert
{
    JELOG2(EPim);
    // convert fields
    CContactItemFieldSet& fieldSet = aCard.CardFields();
    TInt fieldCount = fieldSet.Count();
    for (TInt i = 0; i < fieldCount; i++)
    {
        ConvertFieldL(fieldSet[i], aItem);
    }
    // convert categories
    MPIMItemData& item = aItem.ItemData();
    CContactIdArray* groupArray = aCard.GroupsJoinedLC();
    TInt groupCount = groupArray->Count();
    for (TInt i = 0; i < groupCount; i++)
    {
        HBufC* label = iCategoryManager.GroupLabelL((*groupArray)[i]);
        CleanupStack::PushL(label);
        // Add item to this category if it doesn't belong to it already
        if (item.FindCategory(*label) == KErrNotFound)
        {
            item.AddNewCategoryL(*label);
        }
        CleanupStack::PopAndDestroy(label);
    }
    CleanupStack::PopAndDestroy(groupArray);
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::UpdateCardL
// Updates the data in the Contacts Model contact card to match
// the data in the PIM item data container.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::UpdateCardL(const MPIMContactItem& aItem, // Item from which the data is read
        CContactCard& aCard) // The card which is updated
{
    JELOG2(EPim);
    CleanCard(aCard);
    const MPIMItemData& itemData = aItem.ItemData();
    // Get fields
    CArrayFix<TPIMField>* fields = itemData.FieldsLC();
    TInt fieldCount = fields->Count();
    for (TInt i = 0; i < fieldCount; i++)
    {
        ConvertFieldL(aItem, aCard, (TPIMContactField)(fields->At(i)));
    }
    CleanupStack::PopAndDestroy(); // fields
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::UpdateCategoriesL
// Updates the category information in the database.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::UpdateCategoriesL(const MPIMContactItem& aItem, // Item containing the new categories
        const CContactCard& aCard) // Item to update
{
    JELOG2(EPim);
    const CDesCArray& pimCategories = aItem.ItemData().Categories();
    const TInt pimCategoryCount = pimCategories.Count();
    CArrayFix<TInt>* newCategoryTable = new(ELeave) CArrayFixFlat<TInt> (pimCategoryCount);
    CleanupStack::PushL(newCategoryTable);    
    TInt i = 0;
    for (i = 0; i < pimCategoryCount; i++)
    {
        newCategoryTable->InsertL(TRUE,i);
    }

    CContactIdArray* cardCategories = aCard.GroupsJoinedLC();
    TContactItemId id = aItem.ContactItemIdL();

    // first remove extra groups
    const TInt cardCategoryCount = cardCategories->Count();
    for (i = 0; i < cardCategoryCount; i++)
    {
        TContactItemId groupId = (*cardCategories)[i];
        HBufC* groupLabel = iCategoryManager.GroupLabelL(groupId);
        TInt pos;
        TInt found = pimCategories.Find(*groupLabel, pos, ECmpNormal);
        delete groupLabel;
        if (found != 0)
        {
            // removed group
            iCategoryManager.RemoveFromGroupL(id, groupId);
        }
        else
        {
			// old group
            newCategoryTable->InsertL(FALSE,pos);
        }
    }
    // then add new categories
    for (i = 0; i < pimCategoryCount; i++)
    {
        if (newCategoryTable->At(i))
        {
            TPtrC category = pimCategories[i];
            iCategoryManager.AddToGroupL(id, category);
        }
    }
    CleanupStack::PopAndDestroy(2); // cardCategories, newCategoryTable
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ContactsModelFieldType
// Other items were commented in the header
// -----------------------------------------------------------------------------
//
CArrayFix<TInt>* CPIMContactItemAdapter::ContactsModelFieldTypeL(
    TPIMContactField aContactField) const
{
    JELOG2(EPim);
    return iFieldInfo->MatchPIMFieldL(aContactField);
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::CPIMContactItemAdapter
// C++ constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CPIMContactItemAdapter::CPIMContactItemAdapter(
    CPIMContactCategoryManager& aCategoryManager) :
        iCategoryManager(aCategoryManager)
{
    JELOG2(EPim);
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
inline void CPIMContactItemAdapter::ConstructL()
{
    JELOG2(EPim);
    iFieldInfo = CPIMContactFieldInfo::NewL();
    iImageScaler = CPIMImageScaler::NewL();
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::CleanCardL
// Deletes all the supported fields from the contact card.
// Those fields which are not supported are left as is.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::CleanCard(CContactCard& aCard)
{
    JELOG2(EPim);
    // convert fields
    CContactItemFieldSet& fieldSet = aCard.CardFields();
    TInt fieldCount = fieldSet.Count();
    for (TInt i = 0; i < fieldCount; i++)
    {
        if (IsSupportedField(fieldSet[i]))
        {
            // as the field is removed, the array shrinks
            aCard.RemoveField(i--);
            fieldCount--;
        }
    }
}
// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::IsSupportedFieldL
// Checks if we support this particular contact field.
// Returns:     ETrue if the field is supported
//              EFalse otherwise
// -----------------------------------------------------------------------------
//
TBool CPIMContactItemAdapter::IsSupportedField(CContactItemField& aField)
{
    JELOG2(EPim);
    const CContentType& type = aField.ContentType();
    if (type.FieldTypeCount() == 0)
    {
        // we don't support unknown types
        return EFalse;
    }

    TFieldType fieldTypeUid = type.FieldType(0);
    TInt fieldType = fieldTypeUid.iUid;
    switch (fieldType)
    {
    case KUidContactFieldAddressValue:
    {
        // Label addresses are also stored in the database using
        // KUidContactFieldAddressValue, but we don't support them
        if (type.ContainsFieldType(KUidContactFieldVCardMapADR))
        {
            return ETrue;
        }
        else
        {
            return EFalse;
        }
    }
    case KUidContactFieldPictureValue:
    {
        // Thumbnails are supported while pictures are not
        if (type.ContainsFieldType(KUidContactFieldVCardMapPHOTO))
        {
            return ETrue;
        }
        else
        {
            return EFalse;
        }
    }
    case KUidContactFieldSIPIDValue:
    case KUidContactFieldFamilyNameValue:
    case KUidContactFieldGivenNameValue:
    case KUidContactFieldSecondNameValue:
    case KUidContactFieldFamilyNamePronunciationValue:
    case KUidContactFieldGivenNamePronunciationValue:
    case KUidContactFieldAdditionalNameValue:
    case KUidContactFieldPrefixNameValue:
    case KUidContactFieldSuffixNameValue:
    case KUidContactFieldPhoneNumberValue:
    case KUidContactFieldFaxValue:
    case KUidContactFieldEMailValue:
    case KUidContactFieldUrlValue:
    case KUidContactFieldPostOfficeValue:
    case KUidContactFieldExtendedAddressValue:
    case KUidContactFieldPostCodeValue:
    case KUidContactFieldLocalityValue:
    case KUidContactFieldRegionValue:
    case KUidContactFieldCountryValue:
    case KUidContactFieldCompanyNameValue:
    case KUidContactFieldNoteValue:
    case KUidContactFieldBirthdayValue:
    case KUidContactFieldJobTitleValue:
    case KUidContactFieldDTMFValue:
    case KUidContactFieldIMAddressValue:
    case KUidContactFieldDepartmentNameValue:
    case KUidContactFieldAssistantValue:
    case KUidContactFieldChildrenValue:
    case KUidContactFieldSpouseValue:
    case KUidContactFieldAnniversaryValue:
    case KUidContactFieldClassValue:
    {
        return ETrue;
    }
    default:
    {
        return EFalse;
    }
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ConvertFieldL
// Converts a field from a PIM Item to a Contact Card.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::ConvertFieldL(const MPIMContactItem& aItem, // item to read the field from
        CContactCard& aCard, // card to write the field to
        TPIMContactField aField) // field to convert
{
    JELOG2(EPim);
    const MPIMItemData& itemData = aItem.ItemData();

    // These are indexes in PIM Item address values
    TInt plainAddressIndex = KErrNotFound;
    TInt homeAddressIndex = KErrNotFound;
    TInt workAddressIndex = KErrNotFound;
    if (aField == EPIMContactAddr)
    {
        AddressAttributesL(itemData, plainAddressIndex, homeAddressIndex,
                           workAddressIndex);
        ConvertAddressFieldL(itemData, aCard, aField, plainAddressIndex,
                             KPIMAttrNone);
        ConvertAddressFieldL(itemData, aCard, aField, homeAddressIndex,
                             EPIMContactAttrHome);
        ConvertAddressFieldL(itemData, aCard, aField, workAddressIndex,
                             EPIMContactAttrWork);
        return;
    }

    TInt valueCount = itemData.CountValues(aField);
    for (TInt i = 0; i < valueCount; i++)
    {
        switch (aField)
        {
        case EPIMContactUid:
        case EPIMContactRevision:
        {
            // uid and revision fields are totally ignored
            break;
        }
        case EPIMContactBirthday:
        case EPIMContactExtAnniversary:
        {
            ConvertDateFieldL(itemData, aCard, aField, i);
            break;
        }
        case EPIMContactEmail:
        case EPIMContactNickname:
        case EPIMContactNote:
        case EPIMContactOrg:
        case EPIMContactTel:
        case EPIMContactTitle:
        case EPIMContactUrl:
        case EPIMContactExtSip:
        case EPIMContactExtDtmf:
        case EPIMContactExtWvUserId:
        case EPIMContactExtDepartment:
        case EPIMContactExtAssistantName:
        case EPIMContactExtChildren:
        case EPIMContactExtSpouse:
        case EPIMContactExtVoip:
        case EPIMContactExtPTT:
        case EPIMContactExtSWIS:
        {
            ConvertStringFieldL(itemData, aCard, aField, i);
            break;
        }
        case EPIMContactName:
        {
            ConvertNameFieldL(itemData, aCard, aField, i);
            break;
        }
        case EPIMContactPhoto:
        {
            ConvertPhotoFieldL(itemData, aCard, aField, i);
            break;
        }
        case EPIMContactClass:
        {
            ConvertClassFieldL(itemData, aCard, aField, i);
            break;
        }
        case EPIMContactFormattedName:
        case EPIMContactFormattedAddr:
        case EPIMContactPhotoUrl:
        case EPIMContactPublicKey:
        case EPIMContactPublicKeyString:
        default:
        {
            __ASSERT_DEBUG(EFalse, User::Panic(KPIMPanicCategory,
                                               EPIMPanicUnsupportedField));
            return;
        }
        }
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ConvertFieldL
// Converts a field from a Contact Card to a PIM Item.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::ConvertFieldL(const CContactItemField& aField, // field to read data from
        MPIMContactItem& aItem) // field to write the data to
{
    JELOG2(EPim);
    TStorageType type = aField.StorageType();
    switch (type)
    {
    case KStorageTypeText:
    {
        ConvertTextFieldL(aField, aItem);
        break;
    }
    case KStorageTypeStore:
    {
        ConvertStoreFieldL(aField, aItem);
        break;
    }
    case KStorageTypeContactItemId:
    {
        break;
    }
    case KStorageTypeDateTime:
    {
        ConvertDateFieldL(aField, aItem);
        break;
    }
    default:
    {
        __ASSERT_DEBUG(EFalse, User::Panic(KPIMPanicCategory,
                                           EPIMPanicUnsupportedField));
    }
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ConvertTextFieldL
// Converts a text field from a Contact Card to a PIM Item.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::ConvertTextFieldL(const CContactItemField& aField, // field to read data from
        MPIMContactItem& aItem) // field to write the data to
{
    JELOG2(EPim);
    MPIMItemData& itemData = aItem.ItemData();
    CContactTextField* textField = aField.TextStorage();
    const CContentType& type = aField.ContentType();

    // Check that can we handle this field
    if (!(textField->IsFull()) || (type.FieldTypeCount() == 0))
    {
        return;
    }

    TPtrC text = textField->Text();
    TFieldType fieldTypeUid = type.FieldType(0);
    TPIMAttribute attributes = KPIMAttrNone;

    TPIMField field = iFieldInfo->MatchContactField(fieldTypeUid.iUid);
    // Check which field we must handle
    switch (field)
    {
    case EPIMContactName:
    {
        TPIMContactNameElement
        nameElement =
            static_cast<TPIMContactNameElement>(iFieldInfo->MatchContactArrayElement(
                                                    fieldTypeUid.iUid));
        AddNameFieldToItemL(text, aItem, nameElement, aField.Label());
        break;
    }
    case EPIMContactAddr:
    {
        TPIMContactAddrElement
        addrElement =
            static_cast<TPIMContactAddrElement>(iFieldInfo->MatchContactArrayElement(
                                                    fieldTypeUid.iUid));
        attributes = iFieldInfo->FieldAttributes(aField.ContentType());

        // Label addresses are also stored in the database using
        // KUidContactFieldAddressValue, but we don't support them
        if (fieldTypeUid.iUid == KUidContactFieldAddressValue)
        {
            if (type.ContainsFieldType(KUidContactFieldVCardMapADR))
            {
                AddAddressFieldToItemL(text, aItem, EPIMContactAddrStreet,
                                       attributes, aField.Label());
            }
        }
        else
        {
            AddAddressFieldToItemL(text, aItem, addrElement, attributes,
                                   aField.Label());
        }
        break;
    }
    case EPIMContactClass:
    {
        AddClassFieldToItemL(itemData, text, aField.Label());
        break;
    }
    case EPIMContactExtSip:
    case EPIMContactExtVoip:
    case EPIMContactExtPTT:
    case EPIMContactExtSWIS:
    {
        // SIPID value can hold PoC (Push-To-Talk) Share view, SIP
        // and VOIP (Voice Over IP) fields inside it.
        // Apply internal attributes
        CArrayFix<TUid>* attrs = new(ELeave) CArrayFixFlat<TUid> (3);
        CleanupStack::PushL(attrs);
        iFieldInfo->GetFieldInternalAttributesL(type, *attrs);
        // VOIP (Internet telephone) field
        if (type.ContainsFieldType(KUidContactFieldVCardMapVOIP))
        {
            attributes = iFieldInfo->FieldAttributes(type);
            field = EPIMContactExtVoip;
        }
        else if (type.ContainsFieldType(KUidContactFieldVCardMapPOC))
        {
            field = EPIMContactExtPTT;
        }
        else if (type.ContainsFieldType(KUidContactFieldVCardMapSWIS))
        {
            field = EPIMContactExtSWIS;
        }
        else
        {
            field = EPIMContactExtSip;
        }
        // Add field, attributes, internal attributes and label to the item
        // Note that internal attributes are popped from the cleanup stack
        // inside the following function and there is no need to pop after it
        AddStringFieldWithLabelL(itemData, field, attributes, attrs, text,
                                 aField.Label());
        break;
    }
    case EPIMContactEmail:
    case EPIMContactNickname:
    case EPIMContactNote:
    case EPIMContactOrg:
    case EPIMContactTel:
    case EPIMContactTitle:
    case EPIMContactUrl:
    case EPIMContactExtDtmf:
    case EPIMContactExtWvUserId:
    case EPIMContactExtDepartment:
    case EPIMContactExtAssistantName:
    case EPIMContactExtChildren:
    case EPIMContactExtSpouse:
    {
        // Get internal attributes from the content type
        CArrayFix<TUid>* attrs = new(ELeave) CArrayFixFlat<TUid> (3);
        CleanupStack::PushL(attrs);
        iFieldInfo->GetFieldInternalAttributesL(type, *attrs);
        // Add attributes. FAX is special so it must be handled separately
        attributes = iFieldInfo->FieldAttributes(aField.ContentType());
        if (fieldTypeUid.iUid == KUidContactFieldFaxValue)
        {
            attributes |= EPIMContactAttrFax;
        }
        // Add field, attributes, internal attributes and label to the item
        // Note that internal attributes are popped from the cleanup stack
        // inside the following function and there is no need to pop after it
        AddStringFieldWithLabelL(itemData, field, attributes, attrs, text,
                                 aField.Label());
        break;
    }
    default:
    {
        // The field is not supported so we don't know what to do with it
        // and it is ignored. NOTE that contacts are opened first before
        // writing those to the contact database to ensure that unsupported
        // fields will still be included the to item and no data will be lost
        break;
    }
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::AddStringFieldWithLabelL
// Adds a string field to the item, and optionally sets the fields label.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::AddStringFieldWithLabelL(MPIMItemData& aItem,
        TPIMField aField, TPIMAttribute aAttributes,
        CArrayFix<TUid>* aInternalAttributes, const TPtrC aValue,
        const TPtrC aLabel)
{
    JELOG2(EPim);
    TInt valueCount = aItem.CountValues(aField);
    TPIMFieldData fieldData(aField, aAttributes, aValue.AllocLC());

    // Add data to the item
    aItem.AddValueL(fieldData);
    CleanupStack::Pop(); // aValue.AlloclC()
    // Apply internal attributes
    aItem.SetInternalAttributesL(aField, valueCount, aInternalAttributes);
    // aItem now owns aInternalAttributes
    CleanupStack::Pop(aInternalAttributes);

    // Set preferred index if it is set to this field value index
    // Only one value index of this field can have preferred index set
    // so we have to remove all existing preferred indexes. This has to
    // be done to SMS attribute also. There cannot be more than one
    // preferred SMS number in the S60 Phonebook
    if (aAttributes & EPIMContactAttrPreferred || aAttributes
            & EPIMContactAttrSms)
    {
        // Clear "preferred index" attribute from all other values
        // except this one (previously added one)
        for (TInt i = 0; i < valueCount; i++)
        {
            TPIMAttribute attributes = aItem.AttributesL(aField, i);
            // Clear attributes from preferred index
            if (aAttributes & EPIMContactAttrPreferred)
            {
                attributes &= ~EPIMContactAttrPreferred;
            }
            if (aAttributes & EPIMContactAttrSms)
            {
                attributes &= ~EPIMContactAttrSms;
            }
            aItem.SetAttributesL(aField, i, attributes);
        }
    }

    // Add label if necessary
    if (aLabel.Length() > 0)
    {
        aItem.SetLabelL(aField, valueCount, 0, aLabel);
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ConvertDateFieldL
// Converts a date field from a Contact Card to a PIM Item.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::ConvertDateFieldL(const CContactItemField& aField, // field to read data from
        MPIMContactItem& aItem) // field to write the data to
{
    JELOG2(EPim);
    MPIMItemData& itemData = aItem.ItemData();
    CContactDateField* dateField = aField.DateTimeStorage();
    if (!(dateField->IsFull()))
    {
        // if date is not set, we don't read it
        return;
    }
    TTime time = dateField->Time();
    const CContentType& type = aField.ContentType();

    TPIMField field(0);
    // Check the field type
    if (type.ContainsFieldType(KUidContactFieldBirthday))
    {
        field = EPIMContactBirthday;
    }
    else if (type.ContainsFieldType(KUidContactFieldAnniversary))
    {
        field = EPIMContactExtAnniversary;
    }

    // Anniversary and Birthday are only dates so we are not interested
    // in which is the time in these fields. This is because Phonebook
    // doesn't take into account what time is used within these fields
    // Time conversion between UTC and local time is not needed here
    // because Phonebook handles only dates and PIM API times are in UTC
    // format causing this date to be set to the start of the UTC date time
    // and the date is shown correctly when midlet accesses it on the Java-side
    TDateTime temp(time.DateTime());
    temp.SetHour(0);
    temp.SetSecond(0);
    temp.SetMinute(0);
    temp.SetMicroSecond(0);

    // Count values and add new data
    TInt valueCount = itemData.CountValues(field);
    TPIMFieldData fieldData(field, KPIMAttrNone, TPIMDate(temp));
    itemData.AddValueL(fieldData);

    // Set label
    if (aField.Label().Length() > 0)
    {
        itemData.SetLabelL(field, valueCount, 0, aField.Label());
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ConvertStoreFieldL
// Converts a store field from a Contact Card to a PIM Item.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::ConvertStoreFieldL(
    const CContactItemField& aField, // field to read data from
    MPIMContactItem& aItem) // field to write the data to
{
    JELOG2(EPim);
    CContactStoreField* storeField = aField.StoreStorage();
    if (!(storeField->IsFull()))
    {
        return;
    }
    const CContentType& type = aField.ContentType();
    if (type.ContainsFieldType(KUidContactFieldPicture))
    {
        // We don't care about other store fields
        MPIMItemData& itemData = aItem.ItemData();
        HBufC8* data = storeField->Thing();
        if (data)
        {
            TInt dataLength = data->Length();
            CPIMByteArray* byteArray = new(ELeave) CPIMByteArray(dataLength);
            CleanupStack::PushL(byteArray);
            byteArray->AppendL(data->Ptr(), dataLength);
            TPIMFieldData fieldData(EPIMContactPhoto, KPIMAttrNone, byteArray);
            itemData.AddValueL(fieldData);
            CleanupStack::Pop(); // byteArray
        }
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::AddNameFieldToItemL
// Adds a name field to a PIM Item
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::AddNameFieldToItemL(TPtrC aText, // text to add
        MPIMContactItem& aItem, // item to add to
        TPIMContactNameElement aElement, // element to add
        TPtrC aLabel) // label of the field
{
    JELOG2(EPim);
    MPIMItemData& itemData = aItem.ItemData();
    // 7 for array granularity
    CDesCArrayFlat* nameArray = new(ELeave) CDesC16ArrayFlat(7);
    CleanupStack::PushL(nameArray);
    // do we already have a name field in the item
    TInt nameCount = itemData.CountValues(EPIMContactName);
    if (nameCount > 0)
    {
        const TPIMFieldData fieldData = itemData.ValueL(EPIMContactName, 0);
        const CDesCArray& oldNameArray = fieldData.StringArrayValue();
        TInt elementCount = oldNameArray.Count();

        for (TInt i = 0; i < elementCount; i++)
        {
            TPtrC16 oldName = oldNameArray[i];
            nameArray->AppendL(oldName);
        }
        // don't trust that item data has correct amount of elements
        for (TInt i = elementCount; i < EPIMContactNameNumElements; i++)
        {
            nameArray->AppendL(KPIMNullArrayElement());
        }
        // Add new element
        nameArray->Delete(aElement);
        nameArray->InsertL(aElement, aText);
        TPIMFieldData newFieldData(EPIMContactName, KPIMAttrNone, nameArray);
        itemData.SetValueL(newFieldData);
    }
    else
    {
        for (TInt i = 0; i < EPIMContactNameNumElements; i++)
        {
            nameArray->AppendL(KPIMNullArrayElement());
        }
        // Add new element
        nameArray->Delete(aElement);
        nameArray->InsertL(aElement, aText);
        TPIMFieldData newFieldData(EPIMContactName, KPIMAttrNone, nameArray);
        itemData.AddValueL(newFieldData);
    }
    // Name array is now owned by the item
    CleanupStack::Pop(nameArray);
    // Add label if it exists
    if (aLabel.Length() > 0)
    {
        itemData.SetLabelL(EPIMContactName, 0, aElement, aLabel);
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::AddAddressFieldToItemL
// Adds an address field to a PIM Item
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::AddAddressFieldToItemL(TPtrC aText, // text to add
        MPIMContactItem& aItem, // item to add to
        TPIMContactAddrElement aElement, // element to add
        TPIMAttribute aAttributes, // attributes of the address
        TPtrC aLabel) // label
{
    JELOG2(EPim);
    MPIMItemData& itemData = aItem.ItemData();
    // 7 for array granularity
    CDesCArrayFlat* addrArray = new(ELeave) CDesC16ArrayFlat(7);
    CleanupStack::PushL(addrArray);
    // do we already have an address field in the item with the same attributes
    TInt addrCount = itemData.CountValues(EPIMContactAddr);
    TBool notFound = ETrue;
    TInt index = -1;
    for (TInt i = 0; (notFound) && (i < addrCount); i++)
    {
        if (itemData.AttributesL(EPIMContactAddr, i) == aAttributes)
        {
            const TPIMFieldData fieldData = itemData.ValueL(EPIMContactAddr, i);
            const CDesCArray& oldAddrArray = fieldData.StringArrayValue();
            TInt elementCount = oldAddrArray.Count();
            for (TInt j = 0; j < elementCount; j++)
            {
                TPtrC oldAddr = oldAddrArray[j];
                addrArray->AppendL(oldAddr);
            }
            for (TInt j = elementCount; j < EPIMContactAddrNumElements; j++)
            {
                addrArray->AppendL(KPIMNullArrayElement());
            }
            notFound = EFalse;
            addrArray->Delete(aElement);
            addrArray->InsertL(aElement, aText);
            TPIMFieldData newFieldData(EPIMContactAddr, aAttributes, addrArray);
            newFieldData.SetIndex(i);
            itemData.SetValueL(newFieldData);
            index = i;
        }
    }
    if (notFound)
    {
        for (TInt i = 0; i < EPIMContactAddrNumElements; i++)
        {
            addrArray->AppendL(KPIMNullArrayElement);
        }
        addrArray->Delete(aElement);
        addrArray->InsertL(aElement, aText);
        TPIMFieldData fieldData(EPIMContactAddr, aAttributes, addrArray);
        itemData.AddValueL(fieldData);
    }
    // Array is now owned by the item
    CleanupStack::Pop(addrArray);

    // Add label if it exists
    if (aLabel.Length() > 0)
    {
        // If the index is -1, it indicates that we are
        // adding new string array at the end of the others
        // so the index should be same as addrCount
        index = (index == -1) ? addrCount : index;
        itemData.SetLabelL(EPIMContactAddr, index, aElement, aLabel);
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ConvertAddressFieldL
// Converts an address field from a PIM Item to a Contact Card.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::ConvertAddressFieldL(const MPIMItemData& aItem, // item to convert from
        CContactCard& aCard, // card to write to
        TPIMContactField aField, // field to convert
        TInt aIndex, // index to the field
        TPIMAttribute aAttributes) // attributes of the field
{
    JELOG2(EPim);
    if (aIndex == KErrNotFound)
    {
        return;
    }
    const TPIMFieldData fieldData = aItem.ValueL(aField, aIndex);
    const CDesCArray& addrArray = fieldData.StringArrayValue();
    EContactFieldCategory category = EContactCategoryNone;
    if (aAttributes == EPIMContactAttrHome)
    {
        category = EContactCategoryHome;
    }
    if (aAttributes == EPIMContactAttrWork)
    {
        category = EContactCategoryWork;
    }

    TInt addrCount = addrArray.Count();
    for (TInt i = 0; i < addrCount; i++)
    {
        if (addrArray[i].Compare(KPIMNullArrayElement()) == 0)
        {
            // we don't add null elements
            continue;
        }

        EnsureValidAddrElemLengthL(TPIMContactAddrElement(i), addrArray[i]);

        CContentType* type = iFieldInfo->AddressContentTypeL(
                                 static_cast<TPIMContactAddrElement>(i), category);
        CleanupStack::PushL(type);

        CContactItemField* field = CContactItemField::NewLC(KStorageTypeText,
                                   *type);

        // Add label to the field
        TPIMContactAddrElement element = TPIMContactAddrElement(i);
        const TPtrC label = aItem.LabelL(aField, aIndex, element);
        if (label.Compare(KPIMNullArrayElement) != 0)
        {
            field->SetLabelL(label);
        }
        field->SetUserFlags(category);
        // we don't store any label, Contacts Model should use default template
        CContactTextField* textField = field->TextStorage();
        textField->SetTextL(addrArray[i]);
        // Card takes the ownership of the field
        aCard.AddFieldL(*field);
        CleanupStack::Pop(field);
        CleanupStack::PopAndDestroy(type);
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ConvertNameFieldL
// Converts an address field from a PIM Item to a Contact Card.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::ConvertNameFieldL(const MPIMItemData& aItem, // item to convert from
        CContactCard& aCard, // card to write to
        TPIMContactField aField, // field to convert
        TInt aIndex) // index to the field
{
    JELOG2(EPim);
    const TPIMFieldData fieldData = aItem.ValueL(aField, aIndex);
    const CDesCArray& nameArray = fieldData.StringArrayValue();
    EContactFieldCategory category = EContactCategoryHome;

    TInt nameCount = nameArray.Count();
    for (TInt i = 0; i < nameCount; i++)
    {
        if (nameArray[i].Compare(KPIMNullArrayElement()) == 0)
        {
            // we don't add null elements
            continue;
        }

        EnsureValidNameElemLengthL(TPIMContactNameElement(i), nameArray[i]);

        CContentType* type = iFieldInfo->NameContentTypeL(
                                 static_cast<TPIMContactNameElement>(i));
        CleanupStack::PushL(type);

        CContactItemField* field = CContactItemField::NewLC(KStorageTypeText,
                                   *type);

        // Add label to the field. aIndex should be 0 in this case because
        // there are only one string array in the name field
        TPIMContactNameElement element = TPIMContactNameElement(i);
        const TPtrC label = aItem.LabelL(aField, aIndex, element);
        if (label.Compare(KPIMNullArrayElement) != 0)
        {
            field->SetLabelL(label);
        }
        field->SetUserFlags(category);
        // we don't store any label, Contacts Model should use default template
        CContactTextField* textField = field->TextStorage();
        textField->SetTextL(nameArray[i]);
        // Card takes ownership of the field
        aCard.AddFieldL(*field);
        CleanupStack::Pop(field);
        CleanupStack::PopAndDestroy(type);
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ConvertDateFieldL
// Converts a date field from a PIM Item to a Contact Card.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::ConvertDateFieldL(const MPIMItemData& aItem, // item to convert from
        CContactCard& aCard, // card to write to
        TPIMContactField aField, // field to convert
        TInt aIndex) // index to the field
{
    JELOG2(EPim);
    const TPIMFieldData fieldData = aItem.ValueL(aField, aIndex);
    const TPIMDate& date = fieldData.DateValue();
    CContentType* type = NULL;

    switch (aField)
    {
    case EPIMContactBirthday:
    {
        type = CContentType::NewL(KUidContactFieldBirthday,
                                  KUidContactFieldVCardMapBDAY);
        CleanupStack::PushL(type);
        break;
    }
    case EPIMContactExtAnniversary:
    {
        type = CContentType::NewL(KUidContactFieldAnniversary,
                                  KUidContactFieldVCardMapAnniversary);
        CleanupStack::PushL(type);
        break;
    }
    default:
    {
        User::Panic(KPIMPanicCategory, EPIMPanicUnsupportedDateField);
        break;
    }
    }
    CContactItemField* field = CContactItemField::NewLC(KStorageTypeDateTime,
                               *type);
    // set label if set in pim item
    const TPtrC label = aItem.LabelL(aField, aIndex, 0);
    if (label.Compare(KPIMNullArrayElement) != 0)
    {
        field->SetLabelL(label);
    }

    field->SetUserFlags(EContactCategoryOther);
    CContactDateField* dateField = field->DateTimeStorage();

    // PIM API stores dates as UTC time. Phonebook instead expects that dates
    // are stored as in local time format in the Contacts Model database
    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

    // Anniversary and Birthday are only dates so we are not interested
    // in which is the time in these fields. This is because Phonebook
    // doesn't take into account what time is used within these fields
    TDateTime temp(localTime.DateTime());
    temp.SetHour(0);
    temp.SetMinute(0);
    temp.SetSecond(0);
    temp.SetMicroSecond(0);

    dateField->SetTime(TTime(temp));
    // Card takes ownership of the field
    aCard.AddFieldL(*field);
    CleanupStack::Pop(field);
    CleanupStack::PopAndDestroy(type);
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ConvertStringFieldL
// Converts a string field from a PIM Item to a Contact Card.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::ConvertStringFieldL(const MPIMItemData& aItem, // item to convert from
        CContactCard& aCard, // card to write to
        TPIMContactField aField, // field to convert
        TInt aIndex) // index to the field
{
    JELOG2(EPim);
    const CArrayFix<TUid>& attrs = aItem.InternalAttributesL(aField, aIndex);
    const TPIMFieldData fieldData = aItem.ValueL(aField, aIndex);
    const TDesC& string = fieldData.StringValue();
    TPIMAttribute attributes = fieldData.Attributes();
    EContactFieldCategory category = EContactCategoryNone;

    EnsureValidStringValueLengthL(aField, string);

    if ((attributes & EPIMContactAttrHome) != 0)
    {
        category = EContactCategoryHome;
    }
    if ((attributes & EPIMContactAttrWork) != 0)
    {
        category = EContactCategoryWork;
    }
    CContentType* type = iFieldInfo->StringFieldContentTypeL(aField,
                         attributes, category);
    CleanupStack::PushL(type);
    // Apply internal attributes if any
    TInt interalAttrCount = attrs.Count();
    for (TInt i = 0; i < interalAttrCount; i++)
    {
        TFieldType fieldType = attrs.At(i);
        // Check to avoid duplicate field types in the content type
        // this may be possible when unrecognized fields are added to
        // internal attribute list (e.g incorrectly generated ContentType)
        if (!type->ContainsFieldType(fieldType))
        {
            type->AddFieldTypeL(fieldType);
        }
    }
    // Create new contact item field which is a text storage
    CContactItemField* field =
        CContactItemField::NewLC(KStorageTypeText, *type);
    // set label if set in pim item
    const TPtrC label = aItem.LabelL(aField, aIndex, 0);
    if (label.Compare(KPIMNullArrayElement) != 0)
    {
        field->SetLabelL(label);
    }

    field->SetUserFlags(category);
    CContactTextField* textField = field->TextStorage();
    textField->SetTextL(string);
    // Card takes ownership of the field
    aCard.AddFieldL(*field);
    CleanupStack::Pop(field);
    CleanupStack::PopAndDestroy(type);
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ConvertPhotoFieldL
// Converts a photo field from a PIM Item to a Contact Card.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::ConvertPhotoFieldL(const MPIMItemData& aItem, // item to convert from
        CContactCard& aCard, // card to write to
        TPIMContactField aField, // field to convert
        TInt aIndex) // index to the field
{
    JELOG2(EPim);
    const TPIMFieldData fieldData = aItem.ValueL(aField, aIndex);
    const CPIMByteArray& byteArray = fieldData.BinaryValue();
    EContactFieldCategory category = EContactCategoryNone;
    CContentType* type = CContentType::NewL(KUidContactFieldPicture,
                                            KUidContactFieldVCardMapPHOTO);
    CleanupStack::PushL(type);

    CContactItemField* field = CContactItemField::NewLC(KStorageTypeStore,
                               *type);
    field->SetUserFlags(category);

    const TUint8& byteRef = byteArray.At(0);
    const TUint8* bytePtr = &byteRef;
    TPtrC8 data(bytePtr, byteArray.Count());

    HBufC8* thumbNail = iImageScaler->ScaleL(data);
    CleanupStack::PushL(thumbNail);

    CContactStoreField* storeField = field->StoreStorage();
    storeField->SetThingL(*thumbNail);
    CleanupStack::PopAndDestroy(); // thumbNail
    // Card takes ownership of the field
    aCard.AddFieldL(*field);
    CleanupStack::Pop(field);
    CleanupStack::PopAndDestroy(type);
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ConvertClassFieldL
// Converts a CLASS field from a PIM Item to a Contact Card.
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::ConvertClassFieldL(const MPIMItemData& aItemData,
        CContactCard& aCard, TPIMContactField aField, TInt aIndex)
{
    JELOG2(EPim);
    TPtrC classValueDes(KPIMContactClassPrivate());
    const TPIMFieldData fieldData = aItemData.ValueL(aField, aIndex);
    const TInt classValue = fieldData.IntegerValue();
    CContentType* type = CContentType::NewL(KUidContactFieldClass,
                                            KUidContactFieldVCardMapClass);
    CleanupStack::PushL(type);

    switch (classValue)
    {
    case EPIMContactClassPrivate:
    {
        classValueDes.Set(KPIMContactClassPrivate());
        break;
    }
    case EPIMContactClassPublic:
    {
        classValueDes.Set(KPIMContactClassPublic());
        break;
    }
    case EPIMContactClassConfidential:
    {
        classValueDes.Set(KPIMContactClassConfidential());
        break;
    }
    default:
    {
        __ASSERT_DEBUG(EFalse, User::Invariant());
        break; // For a happy compiler
    }
    }
    // Add value to the contact field
    CContactItemField* field =
        CContactItemField::NewLC(KStorageTypeText, *type);
    field->SetUserFlags(EContactCategoryNone);

    // Set label if any
    const TPtrC label = aItemData.LabelL(aField, aIndex, 0);
    if (label.Compare(KPIMNullArrayElement) != 0)
    {
        field->SetLabelL(label);
    }

    CContactTextField* textField = field->TextStorage();
    textField->SetTextL(classValueDes);
    // Card takes ownership of the field
    aCard.AddFieldL(*field);
    CleanupStack::Pop(field);
    CleanupStack::PopAndDestroy(type);
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::ConvertClassFieldL
// Converts a CLASS field from a Contact Card to PIM item
// -----------------------------------------------------------------------------
//
void CPIMContactItemAdapter::AddClassFieldToItemL(MPIMItemData& aItemData,
        TPtrC& aClassDesValue, TPtrC aLabel)
{
    JELOG2(EPim);
    // Syncronisation is private for default
    TPIMContactClassValue classValue = EPIMContactClassPrivate;
    TInt valueCount = aItemData.CountValues(EPIMContactClass);

    if (aClassDesValue.Compare(KPIMContactClassPublic()) == 0)
    {
        classValue = EPIMContactClassPublic;
    }
    else if (aClassDesValue.Compare(KPIMContactClassPrivate()) == 0)
    {
        classValue = EPIMContactClassPrivate;
    }
    else if (aClassDesValue.Compare(KPIMContactClassConfidential()) == 0)
    {
        classValue = EPIMContactClassConfidential;
    }
    TPIMFieldData fieldData(EPIMContactClass, EPIMFieldInt, KPIMAttrNone,
                            classValue);
    aItemData.AddValueL(fieldData);
    // Label if any
    if (aLabel.Length() > 0)
    {
        aItemData.SetLabelL(EPIMContactClass, valueCount, 0, aLabel);
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::AddressAttributesL
// Checks the optimal attributes for the addresses
// -----------------------------------------------------------------------------
void CPIMContactItemAdapter::AddressAttributesL(const MPIMItemData& aItem,
        TInt& aPlainAddressIndex, TInt& aHomeAddressIndex, TInt& aWorkAddressIndex)
{
    JELOG2(EPim);
    aPlainAddressIndex = KErrNotFound;
    aHomeAddressIndex = KErrNotFound;
    aWorkAddressIndex = KErrNotFound;
    TInt addressCount = aItem.CountValues(EPIMContactAddr);

    // first pass - find zero and single attribute values if exist
    TInt i = 0;
    for (; i < addressCount; i++)
    {
        if (aItem.AttributesL(EPIMContactAddr, i) == KPIMAttrNone)
        {
            aPlainAddressIndex = i;
        }
        else if (aItem.AttributesL(EPIMContactAddr, i) == EPIMContactAttrHome)
        {
            aHomeAddressIndex = i;
        }
        else if (aItem.AttributesL(EPIMContactAddr, i) == EPIMContactAttrWork)
        {
            aWorkAddressIndex = i;
        }
    }

    // second pass - fill in the rest
    for (i = 0; i < addressCount; i++)
    {
        if ((aPlainAddressIndex != i) && (aHomeAddressIndex != i)
                && (aWorkAddressIndex != i))
        {
            // this index has not been used yet
            if (KErrNotFound == aWorkAddressIndex)
            {
                aWorkAddressIndex = i;
            }
            else if (KErrNotFound == aHomeAddressIndex)
            {
                aHomeAddressIndex = i;
            }
            else if (KErrNotFound == aPlainAddressIndex)
            {
                aPlainAddressIndex = i;
            }
            else
            {
                // all indexes are filled
                break; // out of the for loop
            }
        }
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::EnsureValidStringValueLengthL
// -----------------------------------------------------------------------------
void CPIMContactItemAdapter::EnsureValidStringValueLengthL(
    TPIMContactField aField, const TDesC& aValue)
{
    JELOG2(EPim);
    const TInt* maxLengthArr = NULL;
    TInt offset = 0;
    TInt arrLen = 0;

    if (aField >= KContactExtStringMaxLengthOffset)
    {
        maxLengthArr = KContactExtStringMaxLengths;
        offset = KContactExtStringMaxLengthOffset;
        arrLen = KContactExtStringMaxLengthsLen;
    }
    else
    {
        maxLengthArr = KContactStringMaxLengths;
        offset = KContactStringMaxLengthsOffset;
        arrLen = KContactStringMaxLengthsLen;
    }

    TInt index = aField - offset;

    __ASSERT_ALWAYS((index >= 0), User::Invariant());
    __ASSERT_ALWAYS((index < arrLen), User::Invariant());

    TInt maxLen = maxLengthArr[index];
    if ((maxLen > 0) && (aValue.Length() > maxLen))
    {
        User::Leave(KErrTooBig);
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::EnsureValidAddrElemLengthL
// -----------------------------------------------------------------------------
void CPIMContactItemAdapter::EnsureValidAddrElemLengthL(
    TPIMContactAddrElement aElemIndex, const TDesC& aElemValue)
{
    JELOG2(EPim);
    __ASSERT_DEBUG((aElemIndex >= 0), User::Invariant());

    __ASSERT_DEBUG((aElemIndex < EPIMContactAddrNumElements), User::Invariant());

    __ASSERT_DEBUG(
        (KContactAddrElemMaxLengths[aElemIndex] != KErrNotSupported),
        User::Invariant());

    if ((KContactAddrElemMaxLengths[aElemIndex] != KErrNotSupported)
            && (aElemValue.Length() > KContactAddrElemMaxLengths[aElemIndex]))
    {
        User::Leave(KErrTooBig);
    }
}

// -----------------------------------------------------------------------------
// CPIMContactItemAdapter::EnsureValidNameElemLengthL
// -----------------------------------------------------------------------------
void CPIMContactItemAdapter::EnsureValidNameElemLengthL(
    TPIMContactNameElement aElemIndex, const TDesC& aElemValue)
{
    JELOG2(EPim);
    __ASSERT_DEBUG((aElemIndex >= 0), User::Invariant());

    __ASSERT_DEBUG((aElemIndex < EPIMContactNameNumElements), User::Invariant());

    __ASSERT_DEBUG(
        (KContactNameElemMaxLengths[aElemIndex] != KErrNotSupported),
        User::Invariant());

    if ((KContactNameElemMaxLengths[aElemIndex] != KErrNotSupported)
            && (aElemValue.Length() > KContactNameElemMaxLengths[aElemIndex]))
    {
        User::Leave(KErrTooBig);
    }
}

// End of File
