/****************************************************************************
**
** 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 <QDebug>
#include <QTimer>
#include "qcontactrequests.h"
#include "qcontactmanagerengine.h"
#include "qcontactrequestworker_p.h"
#include "qcontactwincebackend_p.h"
/*!
* \class QContactRequestWorker
*
* \brief The QContactRequestWorker class provides a common thread worker queue for QContact asynchronous requests.
*
* A QContactRequestWorker consists of a QContactAbstractRequest request queue.
*
* An instance of the QContactRequestWorker class is a thread object which is dedicated for processing asynchronous contact requests.
* All of these requests are instances of derived classes from QContactAbstractRequest. These request instances can be added to, removed/cancelled from the
* worker thread's internal request queue. Once these requests are processed by the worker thread, the related signals will be emitted
* and all these requests' waiting clients will also wake up.
*
* \sa QContactAbstractRequest
*/
/*! Construct an worker thread. */
QContactRequestWorker::QContactRequestWorker()
:QThread(), d(new QContactRequestWorkerData)
{
}
/*! Initializes this QContactRequestWorker from \a other */
QContactRequestWorker::QContactRequestWorker(const QContactRequestWorker& other)
: QThread(), d(other.d)
{
}
/*! Replace the contents of this QContactRequestWorker with \a other */
QContactRequestWorker& QContactRequestWorker::operator=(const QContactRequestWorker& other)
{
// assign
d = other.d;
return *this;
}
/*! Frees the memory used by this QContactRequestWorker, stops the thread and clean up all request elements in the working queue */
QContactRequestWorker::~QContactRequestWorker()
{
stop();
quit();
while (isRunning() && !wait(1))
d->m_newRequestAdded.wakeAll();
}
/*!
* Stops the worker thread. The worker thread may still run a while until it tries to process next request.
* If the worker thread was waiting on an empty queue, wakes it up.
* \sa run()
*/
void QContactRequestWorker::stop()
{
QMutexLocker locker(&d->m_mutex);
d->m_stop = true;
d->m_newRequestAdded.wakeAll();
}
/*!
* Worker thread's main working loop. Runs forever until the \l stop() function called.
* The worker thread initionally waits on the empty request queue, once the request queue is not empty, takes the first element of
* the queue, then calls related request processing functions based on the request type. Once the request was processed, update the request's
* status and wakes up any threads which waiting for this request.
*
* \sa stop()
* \sa processContactFetchRequest()
* \sa processContactLocalIdFetchRequest()
* \sa processContactSaveRequest()
* \sa processContactRemoveRequest()
* \sa processContactRelationshipFetchRequest()
* \sa processContactRelationshipSaveRequest()
* \sa processContactRelationshipRemoveRequest()
* \sa processContactDetailDefinitionFetchRequest()
* \sa processContactDetailDefinitionSaveRequest()
* \sa processContactDetailDefinitionRemoveRequest()
*/
void QContactRequestWorker::run()
{
QContactRequestElement *re;
for(;;) {
d->cleanUpFinishedRequests();
re = d->takeFirstRequestElement();
if (d->m_stop)
break;
Q_ASSERT(re && re->request);
if (re->request->isFinished() || !re->request->manager()) {
removeRequest(re->request);
continue;
}
// Now perform the active request and emit required signals.
Q_ASSERT(re->request->state() == QContactAbstractRequest::ActiveState);
switch (re->request->type()) {
case QContactAbstractRequest::ContactFetchRequest:
processContactFetchRequest(static_cast<QContactFetchRequest*>(re->request));
break;
case QContactAbstractRequest::ContactLocalIdFetchRequest:
processContactLocalIdFetchRequest(static_cast<QContactLocalIdFetchRequest*>(re->request));
break;
case QContactAbstractRequest::ContactSaveRequest:
processContactSaveRequest(static_cast<QContactSaveRequest*>(re->request));
break;
case QContactAbstractRequest::ContactRemoveRequest:
processContactRemoveRequest(static_cast<QContactRemoveRequest*>(re->request));
break;
case QContactAbstractRequest::RelationshipFetchRequest:
processContactRelationshipFetchRequest(static_cast<QContactRelationshipFetchRequest*>(re->request));
break;
case QContactAbstractRequest::RelationshipSaveRequest:
processContactRelationshipSaveRequest(static_cast<QContactRelationshipSaveRequest*>(re->request));
break;
case QContactAbstractRequest::RelationshipRemoveRequest:
processContactRelationshipRemoveRequest(static_cast<QContactRelationshipRemoveRequest*>(re->request));
break;
case QContactAbstractRequest::DetailDefinitionFetchRequest:
processContactDetailDefinitionFetchRequest(static_cast<QContactDetailDefinitionFetchRequest*>(re->request));
break;
case QContactAbstractRequest::DetailDefinitionSaveRequest:
processContactDetailDefinitionSaveRequest(static_cast<QContactDetailDefinitionSaveRequest*>(re->request));
break;
case QContactAbstractRequest::DetailDefinitionRemoveRequest:
processContactDetailDefinitionRemoveRequest(static_cast<QContactDetailDefinitionRemoveRequest*>(re->request));
break;
default:
break;
}//switch
re->condition.wakeAll();
removeRequest(re->request);
}//for
{
QMutexLocker locker(&d->m_mutex);
foreach (QContactRequestElement* re, d->m_requestQueue) {
if (re)
removeRequest(re->request);
}
d->m_requestQueue.clear();
d->m_requestMap.clear();
d->cleanUpFinishedRequests(true);
}
}
/*!
* Returns true if the given \a req successfully added into the working queue, false if not.
* \sa removeRequest()
* \sa cancelRequest()
*/
bool QContactRequestWorker::addRequest(QContactAbstractRequest* req)
{
if (req) {
QMutexLocker locker(&d->m_mutex);
if (!d->m_requestMap.contains(req)) {
QContactRequestElement* re = new QContactRequestElement;
if (re) {
re->request = req;
re->waiting = false;
d->m_requestQueue.enqueue(re);
d->m_requestMap.insert(req, re);
QContactManagerEngine::updateRequestState(req, QContactAbstractRequest::ActiveState);
d->m_newRequestAdded.wakeAll();
return true;
}
}
}
return false;
}
/*!
* Returns true if the given \a req successfully removed from the working queue, false if not.
* \sa addRequest()
*/
bool QContactRequestWorker::removeRequest(QContactAbstractRequest* req)
{
if (req) {
QMutexLocker locker(&d->m_mutex);
QContactRequestElement * re = d->m_requestMap.value(req);
if (re) {
d->m_requestQueue.removeOne(re);
d->m_requestMap.remove(req);
d->m_removedRequests.append(re);
return true;
}
}
return false;
}
/*!
* Update the status of the given request \a req to QContactAbstractRequest::Cancelling, returns true if sucessful, false if not.
* \sa addRequest()
*/
bool QContactRequestWorker::cancelRequest(QContactAbstractRequest* req)
{
if (req) {
QMutexLocker locker(&d->m_mutex);
QContactManagerEngine::updateRequestState(req, QContactAbstractRequest::CanceledState);
return true;
}
return false;
}
/*!
* Blocks the caller until the given request \a req has been completed by the worker thread or worker thread signals that more partial results
* are available for the request, or until \a msecs milliseconds has elapsed.
* If \a msecs is zero, this function will block indefinitely.
* Returns true if the request was cancelled or completed successfully within the given period, otherwise false.
*
* \sa QContactAbstractRequest::waitForFinished(), QContactAbstractRequest::waitForProgress()
*/
bool QContactRequestWorker::waitRequest(QContactAbstractRequest* req, int msecs)
{
bool ret = false;
if (req) {
QContactRequestElement* re = d->m_requestMap.value(req);
if (re) {
QMutexLocker locker(&re->mutex);
re->waiting = true;
if (msecs) {
ret = re->condition.wait(&re->mutex, msecs);
}
ret = re->condition.wait(&re->mutex);
re->waiting = false;
}
}
return ret;
}
/*!
* Processes the QContactFetchRequest \a req
* \sa QContactFetchRequest
*/
void QContactRequestWorker::processContactFetchRequest(QContactFetchRequest* req)
{
if (req->manager()) {
QContactFilter filter = req->filter();
QList<QContactSortOrder> sorting = req->sorting();
QStringList defs = req->fetchHint().detailDefinitionsHint();
QContactManager::Error operationError;
QList<QContact> requestedContacts;
QList<QContactLocalId> requestedContactIds = req->manager()->contactIds(filter, sorting);
operationError = req->manager()->error();
QContactManager::Error tempError;
for (int i = 0; i < requestedContactIds.size(); i++) {
QContact current = req->manager()->contact(requestedContactIds.at(i));
tempError = req->manager()->error();
// check for single error; update total operation error if required
if (tempError != QContactManager::NoError)
operationError = tempError;
// apply the required detail definition restrictions
if (!defs.isEmpty()) {
QList<QContactDetail> allDetails = current.details();
for (int j = 0; j < allDetails.size(); j++) {
QContactDetail d = allDetails.at(j);
if (!defs.contains(d.definitionName())) {
// this detail is not required.
current.removeDetail(&d);
}
}
}
// add the contact to the result list.
requestedContacts.append(current);
}
// update the request with the results.
QContactManagerEngine::updateContactFetchRequest(req, requestedContacts, operationError, QContactAbstractRequest::FinishedState);
}
}
/*!
* Processes the QContactLocalIdFetchRequest \a req
* \sa QContactLocalIdFetchRequest
*/
void QContactRequestWorker::processContactLocalIdFetchRequest(QContactLocalIdFetchRequest* req)
{
if (req->manager()) {
QContactFilter filter = req->filter();
QList<QContactSortOrder> sorting = req->sorting();
QContactManager::Error operationError = QContactManager::NoError;
QList<QContactLocalId> requestedContactIds = req->manager()->contactIds(filter, sorting);
operationError = req->manager()->error();
QContactManagerEngine::updateContactLocalIdFetchRequest(req, requestedContactIds, operationError, QContactAbstractRequest::FinishedState);
}
}
/*!
* Processes the QContactSaveRequest \a req
* \sa QContactSaveRequest
*/
void QContactRequestWorker::processContactSaveRequest(QContactSaveRequest* req)
{
if (req->manager()) {
QList<QContact> contacts = req->contacts();
QContactManager::Error operationError = QContactManager::NoError;
QMap<int, QContactManager::Error> errorMap;
req->manager()->saveContacts(&contacts, &errorMap);
operationError = req->manager()->error();
QContactManagerEngine::updateContactSaveRequest(req, contacts, operationError, errorMap, QContactAbstractRequest::FinishedState);
}
}
/*!
* Processes the QContactRemoveRequest \a req
* \sa QContactRemoveRequest
*/
void QContactRequestWorker::processContactRemoveRequest(QContactRemoveRequest* req )
{
if (req->manager()) {
// this implementation provides scant information to the user
// the operation either succeeds (all contacts matching the filter were removed)
// or it fails (one or more contacts matching the filter could not be removed)
// if a failure occurred, the request error will be set to the most recent
// error that occurred during the remove operation.
QContactManager::Error operationError = QContactManager::NoError;
QList<QContactLocalId> contactsToRemove = req->contactIds();
operationError = req->manager()->error();
QMap<int, QContactManager::Error> errorMap;
for (int i = 0; i < contactsToRemove.size(); i++) {
QContactManager::Error tempError;
req->manager()->removeContact(contactsToRemove.at(i));
tempError = req->manager()->error();
if (tempError != QContactManager::NoError) {
errorMap.insert(i, tempError);
operationError = tempError;
}
}
// there are no results, so just update the status with the error.
QContactManagerEngine::updateContactRemoveRequest(req, operationError, errorMap, QContactAbstractRequest::FinishedState);
}
}
/*!
* Processes the QContactDetailDefinitionFetchRequest \a req
* \sa QContactDetailDefinitionFetchRequest
*/
void QContactRequestWorker::processContactDetailDefinitionFetchRequest(QContactDetailDefinitionFetchRequest* req)
{
if (req->manager()) {
QContactManager::Error operationError = QContactManager::NoError;
QMap<int, QContactManager::Error> errorMap;
QMap<QString, QContactDetailDefinition> requestedDefinitions;
QStringList names = req->definitionNames();
if (names.isEmpty()) {
names = req->manager()->detailDefinitions().keys(); // all definitions.
operationError = req->manager()->error();
}
QContactManager::Error tempError;
for (int i = 0; i < names.size(); i++) {
QContactDetailDefinition current = req->manager()->detailDefinition(names.at(i));
tempError = req->manager()->error();
requestedDefinitions.insert(names.at(i), current);
if (tempError != QContactManager::NoError) {
errorMap.insert(i, tempError);
operationError = tempError;
}
}
// update the request with the results.
QContactManagerEngine::updateDefinitionFetchRequest(req, requestedDefinitions, operationError, errorMap, QContactAbstractRequest::FinishedState);
}
}
/*!
* Processes the QContactDetailDefinitionSaveRequest \a req
* \sa QContactDetailDefinitionSaveRequest
*/
void QContactRequestWorker::processContactDetailDefinitionSaveRequest(QContactDetailDefinitionSaveRequest* req)
{
if (req->manager()) {
QContactManager::Error operationError = QContactManager::NoError;
QMap<int, QContactManager::Error> errorMap;
QList<QContactDetailDefinition> definitions = req->definitions();
QList<QContactDetailDefinition> savedDefinitions;
QContactManager::Error tempError;
for (int i = 0; i < definitions.size(); i++) {
QContactDetailDefinition current = definitions.at(i);
req->manager()->saveDetailDefinition(current);
tempError = req->manager()->error();
savedDefinitions.append(current);
if (tempError != QContactManager::NoError) {
errorMap.insert(i, tempError);
operationError = tempError;
}
}
// update the request with the results.
QContactManagerEngine::updateDefinitionSaveRequest(req, savedDefinitions, operationError, errorMap, QContactAbstractRequest::FinishedState);
}
}
/*!
* Processes the QContactDetailDefinitionRemoveRequest \a req
* \sa QContactDetailDefinitionRemoveRequest
*/
void QContactRequestWorker::processContactDetailDefinitionRemoveRequest(QContactDetailDefinitionRemoveRequest* req)
{
if (req->manager()) {
QStringList names = req->definitionNames();
QContactManager::Error operationError = QContactManager::NoError;
QMap<int, QContactManager::Error> errorMap;
for (int i = 0; i < names.size(); i++) {
QContactManager::Error tempError;
req->manager()->removeDetailDefinition(names.at(i));
tempError = req->manager()->error();
if (tempError != QContactManager::NoError) {
errorMap.insert(i, tempError);
operationError = tempError;
}
}
// there are no results, so just update the status with the error.
QContactManagerEngine::updateDefinitionRemoveRequest(req, operationError, errorMap, QContactAbstractRequest::FinishedState);
}
}
/*!
* Processes the QContactRelationshipFetchRequest \a req
* \sa QContactRelationshipFetchRequest
*/
void QContactRequestWorker::processContactRelationshipFetchRequest(QContactRelationshipFetchRequest* req)
{
if (req->manager()) {
QList<QContactRelationship> allRelationships = req->manager()->relationships(QString(), QContactId(), QContactRelationship::Either);
QContactManager::Error operationError = req->manager()->error();
QList<QContactRelationship> requestedRelationships;
// first criteria: source contact id must be empty or must match
if (req->first() == QContactId()) {
// all relationships match this criteria (zero id denotes "any")
requestedRelationships = allRelationships;
} else {
for (int i = 0; i < allRelationships.size(); i++) {
QContactRelationship currRelationship = allRelationships.at(i);
if (req->first() == currRelationship.first()) {
requestedRelationships.append(currRelationship);
}
}
}
// second criteria: relationship type must be empty or must match
if (!req->relationshipType().isEmpty()) {
allRelationships = requestedRelationships;
requestedRelationships.clear();
for (int i = 0; i < allRelationships.size(); i++) {
QContactRelationship currRelationship = allRelationships.at(i);
if (req->relationshipType() == currRelationship.relationshipType()) {
requestedRelationships.append(currRelationship);
}
}
}
// third criteria: participant must be empty or must match (including role in relationship)
QString myUri = req->manager()->managerUri();
if (req->second() != QContactId()) {
allRelationships = requestedRelationships;
requestedRelationships.clear();
for (int i = 0; i < allRelationships.size(); i++) {
QContactRelationship currRelationship = allRelationships.at(i);
if (currRelationship.second() == req->second()) {
requestedRelationships.append(currRelationship);
}
}
}
// update the request with the results.
QContactManagerEngine::updateRelationshipFetchRequest(req, requestedRelationships, operationError, QContactAbstractRequest::FinishedState);
}
}
/*!
* Processes the QContactRelationshipRemoveRequest \a req
* \sa QContactRelationshipRemoveRequest
*/
void QContactRequestWorker::processContactRelationshipRemoveRequest(QContactRelationshipRemoveRequest* req)
{
if (req->manager()) {
QMap<int, QContactManager::Error> errorMap;
QContactManager::Error operationError = req->manager()->error();
foreach (const QContactRelationship& relationship, req->relationships()) {
QList<QContactRelationship> matchingRelationships = req->manager()->relationships(relationship.relationshipType(), relationship.first(), QContactRelationship::First);
for (int i = 0; i < matchingRelationships.size(); i++) {
QContactManager::Error tempError;
QContactRelationship possibleMatch = matchingRelationships.at(i);
// if the second criteria matches, or is default constructed id, then we have a match and should remove it.
if (relationship.second() == QContactId() || possibleMatch.second() == relationship.second()) {
req->manager()->removeRelationship(matchingRelationships.at(i));
tempError = req->manager()->error();
if (tempError != QContactManager::NoError) {
errorMap.insert(i, tempError);
operationError = tempError;
}
}
}
}
// there are no results, so just update the status with the error.
QContactManagerEngine::updateRelationshipRemoveRequest(req, operationError, errorMap, QContactAbstractRequest::FinishedState);
}
}
/*!
* Processes the QContactRelationshipSaveRequest \a req
* \sa QContactRelationshipSaveRequest
*/
void QContactRequestWorker::processContactRelationshipSaveRequest(QContactRelationshipSaveRequest* req)
{
if (req->manager()) {
QContactManager::Error operationError = QContactManager::NoError;
QMap<int, QContactManager::Error> errorMap;
QList<QContactRelationship> requestRelationships = req->relationships();
QList<QContactRelationship> savedRelationships;
QContactManager::Error tempError;
for (int i = 0; i < requestRelationships.size(); i++) {
QContactRelationship current = requestRelationships.at(i);
req->manager()->saveRelationship(¤t);
tempError = req->manager()->error();
savedRelationships.append(current);
if (tempError != QContactManager::NoError) {
errorMap.insert(i, tempError);
operationError = tempError;
}
}
// update the request with the results.
QContactManagerEngine::updateRelationshipSaveRequest(req, savedRelationships, operationError, errorMap, QContactAbstractRequest::FinishedState);
}
}
QContactRequestElement* QContactRequestWorkerData::takeFirstRequestElement()
{
QMutexLocker locker(&m_mutex);
// take the first pending request and finish it
if (m_requestQueue.isEmpty())
m_newRequestAdded.wait(&m_mutex);
if (!m_requestQueue.isEmpty())
return m_requestQueue.head();
return 0;
}
void QContactRequestWorkerData::cleanUpFinishedRequests(bool waitForAll)
{
QList<QContactRequestElement*> deleteAll;
QMutex mtx;
QWaitCondition wc;
for (;;) {
foreach (QContactRequestElement* re, m_removedRequests) {
if (re->waiting) {
re->condition.wakeAll();
} else {
deleteAll.append(re);
m_removedRequests.removeOne(re);
}
}
foreach (QContactRequestElement* re, deleteAll) {
if (re)
delete re;
}
deleteAll.clear();
if (!waitForAll || m_removedRequests.isEmpty()) {
break;
} else {
QMutexLocker locker(&mtx);
wc.wait(&mtx, 10);
}
}
}
#include "moc_qcontactrequestworker.cpp"