src/versit/qversitcontactimporter_p.cpp
changeset 0 876b1a06bc25
child 5 603d3f8b6302
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/versit/qversitcontactimporter_p.cpp	Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,885 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qversitdefs_p.h"
+#include "qversitcontactimporter_p.h"
+#include "qversitdocument.h"
+#include "qversitproperty.h"
+#include "qmobilityglobal.h"
+
+#include <qcontactmanagerengine.h>
+#include <qcontact.h>
+#include <qcontactdetail.h>
+#include <qcontactname.h>
+#include <qcontactphonenumber.h>
+#include <qcontactaddress.h>
+#include <qcontactemailaddress.h>
+#include <qcontactorganization.h>
+#include <qcontacturl.h>
+#include <qcontactguid.h>
+#include <qcontacttimestamp.h>
+#include <qcontactanniversary.h>
+#include <qcontactbirthday.h>
+#include <qcontactgender.h>
+#include <qcontactnickname.h>
+#include <qcontactavatar.h>
+#include <qcontactgeolocation.h>
+#include <qcontactnote.h>
+#include <qcontactonlineaccount.h>
+#include <qcontactfamily.h>
+#include <qcontactdisplaylabel.h>
+#include <qcontactthumbnail.h>
+#include <qcontactringtone.h>
+
+#include <QHash>
+#include <QFile>
+
+QTM_USE_NAMESPACE
+
+/*!
+ * Constructor.
+ */
+QVersitContactImporterPrivate::QVersitContactImporterPrivate() :
+    mPropertyHandler(NULL),
+    mPropertyHandler2(NULL),
+    mPropertyHandlerVersion(0),
+    mDefaultResourceHandler(new QVersitDefaultResourceHandler),
+    mResourceHandler(mDefaultResourceHandler)
+{
+    // Contact detail mappings
+    int versitPropertyCount =
+        sizeof(versitContactDetailMappings)/sizeof(VersitContactDetailMapping);
+    for (int i=0; i < versitPropertyCount; i++) {
+        QString versitPropertyName =
+            QLatin1String(versitContactDetailMappings[i].versitPropertyName);
+        QPair<QString,QString> contactDetail;
+        contactDetail.first =
+            QLatin1String(versitContactDetailMappings[i].contactDetailDefinitionName);
+        contactDetail.second =
+            QLatin1String(versitContactDetailMappings[i].contactDetailValueKey);
+        mDetailMappings.insert(versitPropertyName,contactDetail);
+    }
+
+    // Context mappings
+    int contextCount = sizeof(versitContextMappings)/sizeof(VersitMapping);
+    for (int i=0; i < contextCount; i++) {
+        mContextMappings.insert(
+            QLatin1String(versitContextMappings[i].versitString),
+            QLatin1String(versitContextMappings[i].contactString));
+    }
+
+    // Subtype mappings
+    int subTypeCount = sizeof(versitSubTypeMappings)/sizeof(VersitMapping);
+    for (int i=0; i < subTypeCount; i++) {
+        mSubTypeMappings.insert(
+            QLatin1String(versitSubTypeMappings[i].versitString),
+            QLatin1String(versitSubTypeMappings[i].contactString));
+    }
+}
+
+/*!
+ * Destructor.
+ */
+QVersitContactImporterPrivate::~QVersitContactImporterPrivate()
+{
+    delete mDefaultResourceHandler;
+}
+
+/*!
+ * Generates a QContact from \a versitDocument.
+ */
+bool QVersitContactImporterPrivate::importContact(
+        const QVersitDocument& document, int contactIndex, QContact* contact,
+        QVersitContactImporter::Error* error)
+{
+    if (document.type() != QVersitDocument::VCard21Type
+        && document.type() != QVersitDocument::VCard30Type) {
+        *error = QVersitContactImporter::InvalidDocumentError;
+        return false;
+    }
+    const QList<QVersitProperty> properties = document.properties();
+    if (properties.size() == 0) {
+        *error = QVersitContactImporter::EmptyDocumentError;
+        return false;
+    }
+
+    // First, do the properties with PREF set so they appear first in the contact details
+    foreach (const QVersitProperty& property, properties) {
+        QStringList typeParameters = property.parameters().values(QLatin1String("TYPE"));
+        if (typeParameters.contains(QLatin1String("PREF"), Qt::CaseInsensitive))
+            importProperty(document, property, contactIndex, contact);
+    }
+    // ... then, do the rest of the properties.
+    foreach (const QVersitProperty& property, properties) {
+        QStringList typeParameters = property.parameters().values(QLatin1String("TYPE"));
+        if (!typeParameters.contains(QLatin1String("PREF"), Qt::CaseInsensitive))
+            importProperty(document, property, contactIndex, contact);
+    }
+
+    contact->setType(QContactType::TypeContact);
+    QContactManagerEngine::setContactDisplayLabel(contact, QVersitContactImporterPrivate::synthesizedDisplayLabel(*contact));
+
+    if (mPropertyHandler2 && mPropertyHandlerVersion > 1) {
+        mPropertyHandler2->documentProcessed(document, contact);
+    }
+
+    return true;
+}
+
+void QVersitContactImporterPrivate::importProperty(
+        const QVersitDocument& document, const QVersitProperty& property, int contactIndex,
+        QContact* contact)
+{
+    if (mPropertyHandler
+        && mPropertyHandlerVersion == 1
+        && mPropertyHandler->preProcessProperty(document, property, contactIndex, contact))
+        return;
+
+    QPair<QString,QString> detailDefinition =
+        mDetailMappings.value(property.name());
+    QString detailDefinitionName = detailDefinition.first;
+
+    QList<QContactDetail> updatedDetails;
+
+    bool success = false;
+
+    // The following functions create and save the details to the contact
+    if (detailDefinitionName == QContactAddress::DefinitionName) {
+        success = createAddress(property, contact, &updatedDetails); // pass in group
+    } else if (detailDefinitionName == QContactName::DefinitionName) {
+        success = createName(property, contact, &updatedDetails);
+    } else if (detailDefinitionName == QContactBirthday::DefinitionName) {
+        success = createBirthday(property, contact, &updatedDetails);
+    } else if (detailDefinitionName == QContactGeoLocation::DefinitionName){
+        success = createGeoLocation(property, contact, &updatedDetails);
+    } else if (detailDefinitionName == QContactOrganization::DefinitionName) {
+        success = createOrganization(property, contact, &updatedDetails);
+    } else if (detailDefinitionName == QContactNickname::DefinitionName) {
+        success = createNicknames(property, contact, &updatedDetails);
+    } else if (detailDefinitionName == QContactRingtone::DefinitionName) {
+        success = createRingtone(property, contact, &updatedDetails);
+    } else if (detailDefinitionName == QContactThumbnail::DefinitionName) {
+        success = createThumbnail(property, contact, &updatedDetails);
+    } else if (detailDefinitionName == QContactTimestamp::DefinitionName) {
+        success = createTimeStamp(property, contact, &updatedDetails);
+    } else if (detailDefinitionName == QContactPhoneNumber::DefinitionName) {
+        success = createPhone(property, contact, &updatedDetails);
+    } else if (detailDefinitionName == QContactAnniversary::DefinitionName) {
+        success = createAnniversary(property, contact, &updatedDetails);
+    } else if (detailDefinitionName == QContactFamily::DefinitionName) {
+        success = createFamily(property, contact, &updatedDetails);
+    } else if (detailDefinitionName == QContactOnlineAccount::DefinitionName) {
+        success = createOnlineAccount(property, contact, &updatedDetails);
+    } else if (detailDefinitionName == QContactTag::DefinitionName) {
+        success = createTags(property, contact, &updatedDetails);
+    } else if (detailDefinitionName == QContactDisplayLabel::DefinitionName) {
+        success = createCustomLabel(property, contact, &updatedDetails);
+    } else {
+        // Look up mDetailMappings for a simple mapping from property to detail.
+        success = createNameValueDetail(property, contact, &updatedDetails);
+    }
+
+    if (mPropertyHandler2 && mPropertyHandlerVersion > 1) {
+        mPropertyHandler2->propertyProcessed(document, property, success, *contact, &updatedDetails);
+    }
+
+    foreach (QContactDetail detail, updatedDetails) {
+        contact->saveDetail(&detail);
+    }
+
+    if (mPropertyHandler && mPropertyHandlerVersion == 1)
+        mPropertyHandler->postProcessProperty(document, property, success, contactIndex, contact);
+}
+/*!
+ * Creates a QContactName from \a property
+ */
+bool QVersitContactImporterPrivate::createName(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    QContactName name;
+    QContactDetail detail = contact->detail(QContactName::DefinitionName);
+    if (!detail.isEmpty()) {
+        // If multiple name properties exist,
+        // discard all except the first occurrence
+        if (!detail.value(QContactName::FieldFirstName).isEmpty())
+            return false;
+        else
+            name = QContactName(static_cast<QContactName>(detail));
+    }
+
+    QVariant variant = property.variantValue();
+    if (property.valueType() != QVersitProperty::CompoundType
+            || variant.type() != QVariant::StringList)
+        return false;
+    QStringList values = variant.toStringList();
+    QString value(takeFirst(values));
+    if (!value.isEmpty())
+        name.setLastName(value);
+    value = takeFirst(values);
+    if (!value.isEmpty())
+        name.setFirstName(value);
+    value = takeFirst(values);
+    if (!value.isEmpty())
+        name.setMiddleName(value);
+    value = takeFirst(values);
+    if (!value.isEmpty())
+        name.setPrefix(value);
+    value = takeFirst(values);
+    if (!value.isEmpty())
+        name.setSuffix(value);
+
+    saveDetailWithContext(updatedDetails, name, extractContexts(property));
+    return true;
+}
+
+/*!
+ * Creates a QContactPhoneNumber from \a property
+ */
+bool QVersitContactImporterPrivate::createPhone(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    Q_UNUSED(contact)
+    QContactPhoneNumber phone;
+    QString value(property.value());
+    if (value.isEmpty())
+        return false;
+    phone.setNumber(property.value());
+    QStringList subTypes(extractSubTypes(property));
+    if (property.name() == QLatin1String("X-ASSISTANT-TEL"))
+        subTypes << QContactPhoneNumber::SubTypeAssistant;
+    if (!subTypes.isEmpty())
+        phone.setSubTypes(subTypes);
+
+    saveDetailWithContext(updatedDetails, phone, extractContexts(property));
+    return true;
+}
+
+/*!
+ * Creates a QContactAddress from \a property
+ */
+bool QVersitContactImporterPrivate::createAddress(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    Q_UNUSED(contact)
+    QContactAddress address;
+
+    QVariant variant = property.variantValue();
+    if (property.valueType() != QVersitProperty::CompoundType
+            || variant.type() != QVariant::StringList)
+        return false;
+    QStringList addressParts = variant.toStringList();
+    QString value(takeFirst(addressParts));
+    if (!value.isEmpty())
+        address.setPostOfficeBox(value);
+    // There is no setter for the Extended Address in QContactAddress:
+    if (!addressParts.isEmpty())
+        addressParts.removeFirst();
+    value = takeFirst(addressParts);
+    if (!value.isEmpty())
+        address.setStreet(value);
+    value = takeFirst(addressParts);
+    if (!value.isEmpty())
+        address.setLocality(value);
+    value = takeFirst(addressParts);
+    if (!value.isEmpty())
+        address.setRegion(value);
+    value = takeFirst(addressParts);
+    if (!value.isEmpty())
+        address.setPostcode(value);
+    value = takeFirst(addressParts);
+    if (!value.isEmpty())
+        address.setCountry(value);
+    QStringList subTypes(extractSubTypes(property));
+    if (!subTypes.isEmpty())
+        address.setSubTypes(subTypes);
+
+    saveDetailWithContext(updatedDetails, address, extractContexts(property));
+    return true;
+}
+
+/*!
+ * Creates a QContactOrganization from \a property
+ */
+bool QVersitContactImporterPrivate::createOrganization(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    QContactOrganization organization;
+    QPair<QString,QString> detailNameAndFieldName =
+        mDetailMappings.value(property.name());
+    QString fieldName = detailNameAndFieldName.second;
+    QList<QContactOrganization> organizations = contact->details<QContactOrganization>();
+    foreach(const QContactOrganization& current, organizations) {
+        if (current.value(fieldName).length() == 0) {
+            organization = current;
+            break;
+        }
+    }
+    if (fieldName == QContactOrganization::FieldName) {
+        setOrganizationNames(organization, property);
+    } else if (fieldName == QContactOrganization::FieldTitle) {
+        organization.setTitle(property.value());
+    } else if (fieldName == QContactOrganization::FieldRole) {
+        organization.setRole(property.value());
+    } else if (fieldName == QContactOrganization::FieldLogoUrl) {
+        setOrganizationLogo(organization, property);
+    } else if (fieldName == QContactOrganization::FieldAssistantName) {
+        organization.setAssistantName(property.value());
+    } else {
+        return false;
+    }
+
+    saveDetailWithContext(updatedDetails, organization, extractContexts(property));
+    return true;
+}
+
+/*!
+ * Set the organization name and department(s) from \a property.
+ */
+void QVersitContactImporterPrivate::setOrganizationNames(
+    QContactOrganization& organization, const QVersitProperty& property) const
+{
+    QVariant variant = property.variantValue();
+    if (property.valueType() == QVersitProperty::CompoundType
+        && variant.type() == QVariant::StringList) {
+        QStringList values = variant.toStringList();
+        QString name(takeFirst(values));
+        if (!name.isEmpty())
+            organization.setName(name);
+        if (!values.isEmpty())
+            organization.setDepartment(values);
+    }
+}
+
+/*!
+ * Set the organization logo from \a property.
+ */
+void QVersitContactImporterPrivate::setOrganizationLogo(
+    QContactOrganization& org, const QVersitProperty& property) const
+{
+    QString location;
+    QByteArray data;
+    saveDataFromProperty(property, &location, &data);
+    if (!location.isEmpty())
+        org.setLogoUrl(QUrl(location));
+}
+
+/*!
+ * Creates a QContactTimeStamp from \a property
+ */
+bool QVersitContactImporterPrivate::createTimeStamp(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    Q_UNUSED(contact)
+    QContactTimestamp timeStamp;
+    QString value(property.value());
+    bool utc = value.endsWith(QLatin1Char('Z'), Qt::CaseInsensitive);
+    if (utc)
+        value.chop(1); // take away z from end;
+
+    QDateTime dateTime = parseDateTime(value,QLatin1String("yyyyMMddThhmmss"));
+    if (!dateTime.isValid())
+        return false;
+    if (utc)
+        dateTime.setTimeSpec(Qt::UTC);
+    timeStamp.setLastModified(dateTime);
+    saveDetailWithContext(updatedDetails, timeStamp, extractContexts(property));
+    return true;
+}
+
+/*!
+ * Creates a QContactAnniversary from \a property
+ */
+bool QVersitContactImporterPrivate::createAnniversary(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    Q_UNUSED(contact)
+    QContactAnniversary anniversary;
+    QDateTime dateTime = parseDateTime(property.value(), QLatin1String("yyyyMMdd"));
+    if (!dateTime.isValid())
+        return false;
+    anniversary.setOriginalDate(dateTime.date());
+    saveDetailWithContext(updatedDetails, anniversary, extractContexts(property));
+    return true;
+}
+
+/*!
+ * Creates a QContactBirthday from \a property
+ */
+bool QVersitContactImporterPrivate::createBirthday(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    Q_UNUSED(contact)
+    QContactBirthday bday;
+    QDateTime dateTime = parseDateTime(property.value(), QLatin1String("yyyyMMdd"));
+    if (!dateTime.isValid())
+        return false;
+    bday.setDate(dateTime.date());
+    saveDetailWithContext(updatedDetails, bday, extractContexts(property));
+    return true;
+}
+
+/*!
+ * Creates QContactNicknames from \a property
+ */
+bool QVersitContactImporterPrivate::createNicknames(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    Q_UNUSED(contact)
+    QVariant variant = property.variantValue();
+    if (property.valueType() != QVersitProperty::ListType
+            || variant.type() != QVariant::StringList)
+        return false;
+    QStringList values = variant.toStringList();
+    QStringList contexts = extractContexts(property);
+    foreach(const QString& value, values) {
+        if (!value.isEmpty()) {
+            QContactNickname nickName;
+            nickName.setNickname(value);
+            saveDetailWithContext(updatedDetails, nickName, contexts);
+        }
+    }
+    return true;
+}
+
+/*!
+ * Creates QContactTags from \a property
+ */
+bool QVersitContactImporterPrivate::createTags(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    Q_UNUSED(contact)
+    QVariant variant = property.variantValue();
+    if (property.valueType() != QVersitProperty::ListType
+            || variant.type() != QVariant::StringList)
+        return false;
+    QStringList values = variant.toStringList();
+    QStringList contexts = extractContexts(property);
+    foreach(const QString& value, values) {
+        if (!value.isEmpty()) {
+            QContactTag tag;
+            tag.setTag(value);
+            saveDetailWithContext(updatedDetails, tag, contexts);
+        }
+    }
+    return true;
+}
+
+/*!
+ * Creates a QContactOnlineAccount from \a property
+ */
+bool QVersitContactImporterPrivate::createOnlineAccount(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    Q_UNUSED(contact)
+    QContactOnlineAccount onlineAccount;
+    QString value(property.value());
+    if (value.isEmpty())
+        return false;
+    onlineAccount.setAccountUri(property.value());
+    if (property.name() == QLatin1String("X-SIP")) {
+        QStringList subTypes = extractSubTypes(property);
+        if (subTypes.isEmpty())
+            subTypes.append(QContactOnlineAccount::SubTypeSip);
+        onlineAccount.setSubTypes(subTypes);
+    } else if (property.name() == QLatin1String("X-IMPP") ||
+               property.name() == QLatin1String("IMPP") ||
+               property.name() == QLatin1String("X-JABBER")) {
+        onlineAccount.setSubTypes(QContactOnlineAccount::SubTypeImpp);
+    }
+
+    saveDetailWithContext(updatedDetails, onlineAccount, extractContexts(property));
+    return true;
+}
+
+/*!
+ * Creates a QContactRingtone from \a property
+ */
+bool QVersitContactImporterPrivate::createRingtone(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    Q_UNUSED(contact)
+    QString location;
+    QByteArray data;
+    if (saveDataFromProperty(property, &location, &data) && !location.isEmpty()) {
+        QContactRingtone ringtone;
+        ringtone.setAudioRingtoneUrl(location);
+        saveDetailWithContext(updatedDetails, ringtone, extractContexts(property));
+        return true;
+    }
+    return false;
+}
+
+/*!
+ * Creates a QContactAvatar from \a property
+ */
+bool QVersitContactImporterPrivate::createThumbnail(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    QString location;
+    QByteArray data;
+    bool success = false;
+
+    if (saveDataFromProperty(property, &location, &data) && !location.isEmpty()) {
+        QContactAvatar avatar;
+        avatar.setImageUrl(location);
+        saveDetailWithContext(updatedDetails, avatar, extractContexts(property));
+        success = true;
+    }
+    if (!data.isEmpty()) {
+        QImage image;
+        if (image.loadFromData(data)) {
+            QContactThumbnail thumbnail = contact->detail<QContactThumbnail>();
+            // In the case of multiple thumbnails, pick the smallest one.
+            if (thumbnail.isEmpty() || image.byteCount() < thumbnail.thumbnail().byteCount()) {
+                thumbnail.setThumbnail(image);
+            }
+            saveDetailWithContext(updatedDetails, thumbnail, extractContexts(property));
+            success = true;
+        }
+    }
+
+    return success;
+}
+
+/*!
+ * Creates a QContactGeoLocation from \a property
+ */
+bool QVersitContactImporterPrivate::createGeoLocation(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    Q_UNUSED(contact)
+    QContactGeoLocation geo;
+    QVariant variant = property.variantValue();
+    if (property.valueType() != QVersitProperty::CompoundType
+            || variant.type() != QVariant::StringList)
+        return false;
+    QStringList values = variant.toStringList();
+    bool ok1;
+    geo.setLongitude(takeFirst(values).toDouble(&ok1));
+    bool ok2;
+    geo.setLatitude(takeFirst(values).toDouble(&ok2));
+
+    if (ok1 && ok2) {
+        saveDetailWithContext(updatedDetails, geo, extractContexts(property));
+        return true;
+    } else {
+        return false;
+    }
+}
+
+/*!
+ * Creates a QContactFamily from \a property
+ */
+bool QVersitContactImporterPrivate::createFamily(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    QString val = property.value();
+    QContactFamily family = contact->detail<QContactFamily>();
+    if (property.name() == QLatin1String("X-SPOUSE")) {
+        if (val.isEmpty())
+            return false;
+        family.setSpouse(val);
+    } else if (property.name() == QLatin1String("X-CHILDREN")) {
+        QVariant variant = property.variantValue();
+        if (property.valueType() != QVersitProperty::ListType
+                || variant.type() != QVariant::StringList)
+            return false;
+        QStringList values = variant.toStringList();
+        if (values.isEmpty())
+            return false;
+        family.setChildren(values);
+    } else {
+        return false;
+    }
+
+    saveDetailWithContext(updatedDetails, family, extractContexts(property));
+    return true;
+}
+
+/*!
+ * Creates a simple name-value contact detail.
+ */
+bool QVersitContactImporterPrivate::createNameValueDetail(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    Q_UNUSED(contact)
+    QString value(property.value());
+    if (value.isEmpty())
+        return false;
+    QPair<QString,QString> nameAndValueType =
+        mDetailMappings.value(property.name());
+    if (nameAndValueType.first.isEmpty())
+        return false;
+
+    QContactDetail detail(nameAndValueType.first);
+    detail.setValue(nameAndValueType.second, value);
+
+    saveDetailWithContext(updatedDetails, detail, extractContexts(property));
+    return true;
+}
+
+/*!
+ * Find an existing QContactName and set the CustomLabel field on it
+ */
+bool QVersitContactImporterPrivate::createCustomLabel(
+    const QVersitProperty& property,
+    QContact* contact,
+    QList<QContactDetail>* updatedDetails)
+{
+    QString label(property.value());
+    if (!label.isEmpty()) {
+        QContactName name;
+        QContactName existingName = contact->detail<QContactName>();
+        if (!existingName.isEmpty()) {
+            name = existingName;
+        }
+
+        name.setCustomLabel(property.value());
+
+        saveDetailWithContext(updatedDetails, name, extractContexts(property));
+        return true;
+    } else {
+        return false;
+    }
+}
+
+/*!
+ * Extracts the list of contexts from \a types
+ */
+QStringList QVersitContactImporterPrivate::extractContexts(
+    const QVersitProperty& property) const
+{
+    QStringList types = property.parameters().values(QLatin1String("TYPE"));
+    QStringList contexts;
+    foreach (const QString& type, types) {
+        QString value = mContextMappings.value(type.toUpper());
+        if (value.length() > 0)
+            contexts.append(value);
+    }
+    return contexts;
+}
+
+/*!
+ * Extracts the list of subtypes from \a property
+ */
+QStringList QVersitContactImporterPrivate::extractSubTypes(
+    const QVersitProperty& property) const
+{
+    QStringList types = property.parameters().values(QLatin1String("TYPE"));
+    QStringList subTypes;
+    foreach (const QString& type, types) {
+        QString subType = mSubTypeMappings.value(type.toUpper());
+        if (subType.length() > 0)
+            subTypes += subType;
+    }
+    return subTypes;
+}
+
+/*!
+ * Takes the first value in \a list, or an empty QString is if the list is empty.
+ */
+QString QVersitContactImporterPrivate::takeFirst(QList<QString>& list) const
+{
+    return list.empty() ? QString() : list.takeFirst();
+}
+
+/*!
+ * Parses a date and time from text
+ */
+QDateTime QVersitContactImporterPrivate::parseDateTime(
+    const QString& value,
+    const QString& format) const
+{
+    QDateTime dateTime;
+    if (value.contains(QLatin1Char('-'))) {
+        dateTime = QDateTime::fromString(value,Qt::ISODate);
+    } else {
+        dateTime = QDateTime::fromString(value, format);
+    }
+    return dateTime;
+}
+
+/*!
+ * Extracts either a location (URI/filepath) from a \a property, or data (eg. if it was base64
+ * encoded).  If the property contains data, an attempt is made to save it and the location of the
+ * saved resource is recovered to *\a location.  The data is stored into *\a data.
+ */
+bool QVersitContactImporterPrivate::saveDataFromProperty(const QVersitProperty &property,
+                                                            QString *location,
+                                                            QByteArray *data) const
+{
+    bool found = false;
+    const QString valueParam = property.parameters().value(QLatin1String("VALUE"));
+    QVariant variant(property.variantValue());
+    if (variant.type() == QVariant::String
+        || valueParam == QLatin1String("URL")) {
+        *location = property.value();
+        found |= !location->isEmpty();
+    } else if (variant.type() == QVariant::ByteArray) {
+        *data = variant.toByteArray();
+        if (!data->isEmpty()) {
+            found = true;
+            *location = saveContentToFile(property, *data);
+        }
+    }
+    return found;
+}
+
+/*!
+ * Writes \a data to a file and returns the filename.  \a property specifies the context in which
+ * the data was found.
+ */
+QString QVersitContactImporterPrivate::saveContentToFile(
+    const QVersitProperty& property, const QByteArray& data) const
+{
+    QString filename;
+    bool ok = false;
+    if (mResourceHandler)
+        ok = mResourceHandler->saveResource(data, property, &filename);
+    return ok ? filename : QString();
+}
+
+/*!
+ * Adds \a detail to the \a updatedDetails list.  Also sets the contexts to \a contexts if it is not
+ * empty.
+ */
+void QVersitContactImporterPrivate::saveDetailWithContext(
+        QList<QContactDetail>* updatedDetails,
+        QContactDetail detail,
+        const QStringList& contexts)
+{
+    if (!contexts.isEmpty())
+        detail.setContexts(contexts);
+    updatedDetails->append(detail);
+}
+
+/*! Synthesize the display label from the name of the contact, or, failing that, the nickname of
+the contact, or failing that, the organisation of the contact.
+Returns the synthesized display label.
+ */
+QString QVersitContactImporterPrivate::synthesizedDisplayLabel(const QContact& contact)
+{
+    /* XXX This is copied and modified from QContactManagerEngine.  This should be made a public
+       static function in QCME and called here */
+    QList<QContactName> allNames = contact.details<QContactName>();
+
+    const QLatin1String space(" ");
+
+    // synthesize the display label from the name.
+    foreach (const QContactName& name, allNames) {
+        if (!name.customLabel().isEmpty()) {
+            // default behaviour is to allow the user to define a custom display label.
+            return name.customLabel();
+        }
+
+        QString result;
+        if (!name.value(QContactName::FieldPrefix).trimmed().isEmpty()) {
+           result += name.value(QContactName::FieldPrefix);
+        }
+        if (!name.value(QContactName::FieldFirstName).trimmed().isEmpty()) {
+            if (!result.isEmpty())
+                result += space;
+            result += name.value(QContactName::FieldFirstName);
+        }
+        if (!name.value(QContactName::FieldMiddleName).trimmed().isEmpty()) {
+            if (!result.isEmpty())
+                result += space;
+            result += name.value(QContactName::FieldMiddleName);
+        }
+        if (!name.value(QContactName::FieldLastName).trimmed().isEmpty()) {
+            if (!result.isEmpty())
+                result += space;
+            result += name.value(QContactName::FieldLastName);
+        }
+        if (!name.value(QContactName::FieldSuffix).trimmed().isEmpty()) {
+            if (!result.isEmpty())
+                result += space;
+            result += name.value(QContactName::FieldSuffix);
+        }
+        if (!result.isEmpty())
+            return result;
+    }
+
+    QList<QContactNickname> allNicknames = contact.details<QContactNickname>();
+    foreach (const QContactNickname& nickname, allNicknames) {
+        if (!nickname.nickname().isEmpty())
+            return nickname.nickname();
+    }
+
+    /* Well, we had no non empty names. if we have orgs, fall back to those */
+    QList<QContactOrganization> allOrgs = contact.details<QContactOrganization>();
+    foreach (const QContactOrganization& org, allOrgs) {
+        if (!org.name().isEmpty())
+            return org.name();
+    }
+
+    return QString();
+}