diff -r e4ebb16b39ea -r 603d3f8b6302 plugins/versit/backuphandler/backupvcardhandler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/versit/backuphandler/backupvcardhandler.cpp Mon Oct 04 01:37:06 2010 +0300 @@ -0,0 +1,387 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include "backupvcardhandler.h" +#include "qcontact.h" +#include "qcontactdetail.h" +#include "qversitdocument.h" +#include "qversitproperty.h" + +QTM_USE_NAMESPACE + +/* + * This is a map from Versit group names to the details that were generated from properties with the + * said groups. Multiple details can be associated with a single group. + */ +class DetailGroupMap +{ +public: + QList detailsInGroup(const QString& groupName) const; + void insert(const QString& groupName, const QContactDetail& detail); + void update(const QContactDetail& detail); + void clear(); + +private: + QHash mDetailGroupName; // detailid -> group name + QHash mDetailById; // detailid -> detail +}; + +/* See QVersitContactImporter::createBackupHandler() */ +class BackupVCardHandler : public QVersitContactHandler +{ +public: + BackupVCardHandler(); + void propertyProcessed(const QVersitDocument& document, + const QVersitProperty& property, + const QContact& contact, + bool* alreadyProcessed, + QList* updatedDetails); + void documentProcessed(const QVersitDocument& document, + QContact* contact); + void detailProcessed(const QContact& contact, + const QContactDetail& detail, + const QVersitDocument& document, + QSet* processedFields, + QList* toBeRemoved, + QList* toBeAdded); + void contactProcessed(const QContact& contact, + QVersitDocument* document); + +private: + static QVariant deserializeValue(const QVersitProperty& property); + static void serializeValue(QVersitProperty* property, const QVariant& value); + DetailGroupMap mDetailGroupMap; // remembers which details came from which groups + int mDetailNumber; +}; + +Q_DEFINE_LATIN1_CONSTANT(PropertyName, "X-NOKIA-QCONTACTFIELD"); +Q_DEFINE_LATIN1_CONSTANT(DetailDefinitionParameter, "DETAIL"); +Q_DEFINE_LATIN1_CONSTANT(FieldParameter, "FIELD"); +Q_DEFINE_LATIN1_CONSTANT(DatatypeParameter, "DATATYPE"); +Q_DEFINE_LATIN1_CONSTANT(DatatypeParameterVariant, "VARIANT"); +Q_DEFINE_LATIN1_CONSTANT(DatatypeParameterDate, "DATE"); +Q_DEFINE_LATIN1_CONSTANT(DatatypeParameterDateTime, "DATETIME"); +Q_DEFINE_LATIN1_CONSTANT(DatatypeParameterTime, "TIME"); +Q_DEFINE_LATIN1_CONSTANT(DatatypeParameterBool, "BOOL"); +Q_DEFINE_LATIN1_CONSTANT(DatatypeParameterInt, "INT"); +Q_DEFINE_LATIN1_CONSTANT(DatatypeParameterUInt, "UINT"); +Q_DEFINE_LATIN1_CONSTANT(DatatypeParameterUrl, "URL"); +Q_DEFINE_LATIN1_CONSTANT(GroupPrefix, "G"); + +QSet BackupVCardHandlerFactory::profiles() const +{ + QSet retval; + retval.insert(QVersitContactHandlerFactory::ProfileBackup); + return retval; +} + +QString BackupVCardHandlerFactory::name() const +{ + return QLatin1String("com.nokia.qt.mobility.versit.backuphandler"); +} + +int BackupVCardHandlerFactory::index() const +{ + // Prefer to run this plugin last. + return -1; +} + +QVersitContactHandler* BackupVCardHandlerFactory::createHandler() const +{ + return new BackupVCardHandler(); +} + +Q_EXPORT_PLUGIN2(qtversit_backuphandler, BackupVCardHandlerFactory); + +/* + * Returns a list of details generated from a Versit group. + */ +QList DetailGroupMap::detailsInGroup(const QString& groupName) const +{ + QList detailIds = mDetailGroupName.keys(groupName); + QList details; + foreach (int detailId, detailIds) { + details << mDetailById[detailId]; + } + return details; +} + +/* + * Inserts the association between \a detail and \a groupName to the map. + * The detail must have a key (ie. have already been saved in a contact) and the group name must not + * be the empty string. + */ +void DetailGroupMap::insert(const QString& groupName, const QContactDetail& detail) +{ + Q_ASSERT(!groupName.isEmpty()); + mDetailGroupName[detail.key()] = groupName; + mDetailById[detail.key()] = detail; +} + +/* + * Replaces the detail currently in the map with \a detail. + * The detail must have a key (ie. have already been saved in a contact). + */ +void DetailGroupMap::update(const QContactDetail& detail) +{ + Q_ASSERT(detail.key()); + mDetailById[detail.key()] = detail; +} + +/*! + * Removes details and groups from the map. + */ +void DetailGroupMap::clear() +{ + mDetailGroupName.clear(); + mDetailById.clear(); +} + + +BackupVCardHandler::BackupVCardHandler() + : mDetailNumber(0) +{ +} + +void BackupVCardHandler::propertyProcessed( + const QVersitDocument& document, + const QVersitProperty& property, + const QContact& contact, + bool* alreadyProcessed, + QList* updatedDetails) +{ + Q_UNUSED(document) + Q_UNUSED(contact) + QString group; + if (!property.groups().isEmpty()) + group = property.groups().first(); + if (!*alreadyProcessed) { + if (property.name() != PropertyName) + return; + if (property.groups().size() != 1) + return; + QMultiHash parameters = property.parameters(); + QString definitionName = parameters.value(DetailDefinitionParameter); + QString fieldName = parameters.value(FieldParameter); + + // Find a detail previously seen with the same definitionName, which was generated from + // a property from the same group + QContactDetail detail(definitionName); + foreach (const QContactDetail& previousDetail, mDetailGroupMap.detailsInGroup(group)) { + if (previousDetail.definitionName() == definitionName) { + detail = previousDetail; + } + } + // If not found, it's a new empty detail with the definitionName set. + + detail.setValue(fieldName, deserializeValue(property)); + + // Replace the equivalent detail in updatedDetails with the new one + QMutableListIterator it(*updatedDetails); + while (it.hasNext()) { + if (it.next().key() == detail.key()) { + it.remove(); + break; + } + } + updatedDetails->append(detail); + *alreadyProcessed = true; + } + if (!group.isEmpty()) { + // Keep track of which details were generated from which Versit groups + foreach (const QContactDetail& detail, *updatedDetails) { + mDetailGroupMap.insert(group, detail); + } + } +} + +QVariant BackupVCardHandler::deserializeValue(const QVersitProperty& property) +{ + // Import the field + if (property.parameters().contains(DatatypeParameter, DatatypeParameterVariant)) { + // The value was stored as a QVariant serialized in a QByteArray + QDataStream stream(property.variantValue().toByteArray()); + QVariant value; + stream >> value; + return value; + } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterDate)) { + // The value was a QDate serialized as a string + return QDate::fromString(property.value(), Qt::ISODate); + } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterTime)) { + // The value was a QTime serialized as a string + return QTime::fromString(property.value(), Qt::ISODate); + } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterDateTime)) { + // The value was a QDateTime serialized as a string + return QDateTime::fromString(property.value(), Qt::ISODate); + } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterBool)) { + // The value was a bool serialized as a string + return property.value().toInt() != 0; + } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterInt)) { + // The value was an int serialized as a string + return property.value().toInt(); + } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterUInt)) { + // The value was a uint serialized as a string + return property.value().toUInt(); + } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterUrl)) { + // The value was a QUrl serialized as a string + return QUrl(property.value()); + } else { + // The value was stored as a QString or QByteArray + return property.variantValue(); + } +} + +void BackupVCardHandler::documentProcessed( + const QVersitDocument& document, + QContact* contact) +{ + Q_UNUSED(document) + Q_UNUSED(contact) + mDetailGroupMap.clear(); +} + +void BackupVCardHandler::detailProcessed( + const QContact& contact, + const QContactDetail& detail, + const QVersitDocument& document, + QSet* processedFields, + QList* toBeRemoved, + QList* toBeAdded) +{ + Q_UNUSED(contact) + Q_UNUSED(document) + Q_UNUSED(toBeRemoved) + if (detail.accessConstraints().testFlag(QContactDetail::ReadOnly)) + return; + QVariantMap fields = detail.variantValues(); + // fields from the same detail have the same group so the importer can collate them + QString detailGroup = GroupPrefix + QString::number(mDetailNumber++); + int toBeAddedCount = toBeAdded->count(); + bool propertiesSynthesized = false; + for (QVariantMap::const_iterator it = fields.constBegin(); it != fields.constEnd(); it++) { + if (!processedFields->contains(it.key()) && !it.value().toString().isEmpty()) { + // Generate a property for the unknown field + QVersitProperty property; + property.setGroups(QStringList(detailGroup)); + property.setName(PropertyName); + property.insertParameter(DetailDefinitionParameter, detail.definitionName()); + property.insertParameter(FieldParameter, it.key()); + + serializeValue(&property, it.value()); + + toBeAdded->append(property); + propertiesSynthesized = true; + processedFields->insert(it.key()); + } + } + if (propertiesSynthesized) { + // We need to group the already-generated properties with the newly synthesized ones + for (int i = 0; i < toBeAddedCount; i++) { + QVersitProperty& property = (*toBeAdded)[i]; + property.setGroups(property.groups() << detailGroup); + } + } +} + +void BackupVCardHandler::serializeValue(QVersitProperty* property, const QVariant& value) +{ + // serialize the value + if (value.type() == QVariant::String + || value.type() == QVariant::ByteArray) { + // store QStrings and QByteArrays as-is + property->setValue(value); + } else if (value.type() == QVariant::Date) { + // Store a QDate as a string + QString valueString(value.toDate().toString(Qt::ISODate)); + property->insertParameter(DatatypeParameter, DatatypeParameterDate); + property->setValue(valueString); + } else if (value.type() == QVariant::Time) { + // Store a QTime as a string + QString valueString(value.toTime().toString(Qt::ISODate)); + property->insertParameter(DatatypeParameter, DatatypeParameterTime); + property->setValue(valueString); + } else if (value.type() == QVariant::DateTime) { + // Store a QDateTime as a string + QString valueString(value.toDateTime().toString(Qt::ISODate)); + property->insertParameter(DatatypeParameter, DatatypeParameterDateTime); + property->setValue(valueString); + } else if (value.type() == QVariant::Bool) { + // Store an int as a string + QString valueString(QString::number(value.toBool() ? 1 : 0)); + property->insertParameter(DatatypeParameter, DatatypeParameterBool); + property->setValue(valueString); + } else if (value.type() == QVariant::Int) { + // Store an int as a string + QString valueString(QString::number(value.toInt())); + property->insertParameter(DatatypeParameter, DatatypeParameterInt); + property->setValue(valueString); + } else if (value.type() == QVariant::UInt) { + // Store a uint as a string + QString valueString(QString::number(value.toUInt())); + property->insertParameter(DatatypeParameter, DatatypeParameterUInt); + property->setValue(valueString); + } else if (value.type() == QVariant::Url) { + // Store a QUrl as a string + QString valueString(value.toUrl().toString()); + property->insertParameter(DatatypeParameter, DatatypeParameterUrl); + property->setValue(valueString); + } else { + // Store other types by serializing the QVariant in a QByteArray + QByteArray valueBytes; + QDataStream stream(&valueBytes, QIODevice::WriteOnly); + stream << value; + property->insertParameter(DatatypeParameter, DatatypeParameterVariant); + property->setValue(valueBytes); + } +} + +void BackupVCardHandler::contactProcessed( + const QContact& contact, + QVersitDocument* document) +{ + Q_UNUSED(contact) + Q_UNUSED(document) + mDetailNumber = 0; +}