1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtContacts module of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL$
9
** Commercial License Usage
10
** Licensees holding valid commercial Qt licenses may use this file in
11
** accordance with the commercial license agreement provided with the
12
** Software or, alternatively, in accordance with the terms contained in
13
** a written agreement between you and Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
17
** GNU Lesser General Public License Usage
18
** Alternatively, this file may be used under the terms of the GNU Lesser
19
** General Public License version 2.1 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
40
****************************************************************************/
42
#include "qcontact_p.h"
43
#include "qcontactmanagerenginev2wrapper_p.h"
44
#include "qcontactidfilter.h"
45
#include "qcontactabstractrequest_p.h"
46
#include "qcontactfetchbyidrequest.h"
49
QT_BEGIN_NAMESPACE_CONTACTS
51
QContactManagerEngineV2Wrapper::QContactManagerEngineV2Wrapper(QContactManagerEngine *wrappee)
57
QContactManagerEngineV2Wrapper::~QContactManagerEngineV2Wrapper()
62
void QContactManagerEngineV2Wrapper::requestDestroyed(QContactAbstractRequest* req)
64
RequestController* controller = m_controllerForRequest.value(req);
67
// If we own it, just delete the controller (and ignore any subrequests' signals from now on)
69
m_controllerForRequest.insert(req, 0);
71
m_engine->requestDestroyed(req);
75
bool QContactManagerEngineV2Wrapper::startRequest(QContactAbstractRequest* req)
77
if (req && req->type() == QContactAbstractRequest::ContactSaveRequest
78
&& !static_cast<QContactSaveRequest*>(req)->typeMask().isEmpty()) {
79
RequestController* controller;
80
controller = new PartialSaveRequestController(m_engine, this);
81
controller->setRequest(req);
82
connect(controller, SIGNAL(stateChanged(QContactAbstractRequest::State)),
83
this, SLOT(requestStateChanged(QContactAbstractRequest::State)),
84
Qt::QueuedConnection);
85
m_controllerForRequest.insert(req, controller);
86
if (controller->start()) {
87
updateRequestState(req, QContactAbstractRequest::ActiveState);
94
// Otherwise, pass it on
95
return m_engine->startRequest(req);
98
/* A slot connected to the stateChanged signal of a controller object. It is signaled by the
99
controller to indicate that one of the client's requests has an updated state. */
100
void QContactManagerEngineV2Wrapper::requestStateChanged(QContactAbstractRequest::State state)
102
RequestController* controller = qobject_cast<RequestController*>(sender());
103
Q_ASSERT(controller);
104
QContactAbstractRequest* request = controller->request();
106
if (state == QContactAbstractRequest::FinishedState) {
108
if (request) { // It's possible the request was deleted by the sender.
109
// Keep the key in m_controllerForRequest but point it to null to indicate a defunct
111
m_controllerForRequest.insert(request, 0);
114
updateRequestState(request, state);
120
bool QContactManagerEngineV2Wrapper::cancelRequest(QContactAbstractRequest* req)
122
if (m_controllerForRequest.contains(req)) {
123
RequestController* controller = m_controllerForRequest.value(req);
125
// If we own it and it hasn't already been deleted,
126
// just delete the controller (and ignore any subrequests' signals from now on)
128
m_controllerForRequest.insert(req, 0);
132
return m_engine->cancelRequest(req);
137
bool QContactManagerEngineV2Wrapper::waitForRequestFinished(QContactAbstractRequest* req, int msecs)
139
if (m_controllerForRequest.contains(req)) {
140
RequestController* controller = m_controllerForRequest.value(req);
142
if (controller->waitForFinished(msecs)) {
143
updateRequestState(req, QContactAbstractRequest::FinishedState);
149
// A request with a null controller means it has already finished
153
return m_engine->waitForRequestFinished(req, msecs);
158
/* A static helper to twiddle with \a request's privates, setting its engine to \a engine. */
159
void QContactManagerEngineV2Wrapper::setEngineOfRequest(QContactAbstractRequest* request,
160
QContactManagerEngine* engine)
162
request->d_ptr->m_engine = engine;
166
// General RequestController stuff
167
/* A single RequestController is associated with a single client QContactAbstractRequest. It
168
manages the sub-requests that need to be sent to the real engine and controls the asynchronous
169
flow between start() and one or more handleUpdatedSubRequest() calls that might follow.
170
waitForFinished() can also be called on a controller, which synchronously performs the rest of
171
the necessary sub-requests.
174
/* A slot connected the stateChanged signal of a sub request */
175
void RequestController::handleUpdatedSubRequest(QContactAbstractRequest::State state)
177
QObject* caller = sender();
178
QContactAbstractRequest* subRequest = qobject_cast<QContactAbstractRequest*>(caller);
180
if (state == QContactAbstractRequest::FinishedState) {
181
// It's possibly already finished if waitForFinished has previously been called
183
handleFinishedSubRequest(subRequest);
185
// XXX maybe Canceled should be handled
190
/* This function handles the rest of the program flow in a synchronous way. */
191
bool RequestController::waitForFinished(int msecs)
193
// If the current request is active, it must be a ContactFetchRequest. We just need to
194
// wait for it to finish, then finalize the post-processing.
195
if (m_currentSubRequest.isNull()) {
198
while (!isFinished()) {
199
if (!m_currentSubRequest->waitForFinished(msecs))
201
handleFinishedSubRequest(m_currentSubRequest.data());
206
// FetchByIdRequestController stuff
207
/* A FetchByIdRequestController is a RequestController for QContactFetchByIdRequests */
208
bool FetchByIdRequestController::start()
210
// Our strategy is to translate it to a ContactFetchRequest. Later when it finishes, we can
211
// fiddle with the results to get it in the right format.
213
QContactFetchByIdRequest* originalRequest = static_cast<QContactFetchByIdRequest*>(m_request.data());
214
QContactFetchRequest* qcfr = new QContactFetchRequest;
215
QContactIdFilter lif;
216
lif.setIds(originalRequest->contactIds());
217
qcfr->setFilter(lif);
218
qcfr->setFetchHint(originalRequest->fetchHint());
219
// normally, you'd set the manager, but in this case, we only have a bare engine:
220
QContactManagerEngineV2Wrapper::setEngineOfRequest(qcfr, m_engine);
221
m_currentSubRequest.reset(qcfr);
222
connect(qcfr, SIGNAL(stateChanged(QContactAbstractRequest::State)),
223
this, SLOT(handleUpdatedSubRequest(QContactAbstractRequest::State)),
224
Qt::QueuedConnection);
225
return qcfr->start();
228
/* One of our subrequests has finished. Go to the next step. */
229
void FetchByIdRequestController::handleFinishedSubRequest(QContactAbstractRequest* subReq)
231
// For a FetchByIdRequest, we know that the only subrequest is a QContactFetchRequest.
232
// The next step is simply to take the results and reformat it.
234
QContactFetchRequest* qcfr = qobject_cast<QContactFetchRequest*>(subReq);
235
QList<QContact> contacts = qcfr->contacts();
236
QContactManager::Error error = qcfr->error();
238
// Build an index into the results
239
QHash<QContactId, int> idMap; // value is index into unsorted
240
if (error == QContactManager::NoError) {
241
for (int i = 0; i < contacts.size(); i++) {
242
idMap.insert(contacts[i].id(), i);
246
// Find the order in which the results should be presented
247
QContactFetchByIdRequest* request = static_cast<QContactFetchByIdRequest*>(m_request.data());
248
QList<QContactId> ids(request->contactIds());
250
// Build up the results and errors
251
QList<QContact> results;
252
QMap<int, QContactManager::Error> errorMap;
253
for (int i = 0; i < ids.count(); i++) {
254
QContactId id(ids[i]);
255
if (!idMap.contains(id)) {
256
errorMap.insert(i, QContactManager::DoesNotExistError);
257
if (error == QContactManager::NoError)
258
error = QContactManager::DoesNotExistError;
259
results.append(QContact());
261
results.append(contacts[idMap[id]]);
265
// Update the request object
266
QContactManagerEngineV2Wrapper::updateContactFetchByIdRequest(
271
QContactAbstractRequest::FinishedState);
276
// PartialSaveRequestController stuff
277
/* A PartialSaveRequestController is a RequestController for QContactSaveRequests with definition mask */
278
bool PartialSaveRequestController::start()
280
// The first step is to fetch the existing contacts.
281
QList<QContactId> existingContactIds;
282
QList<int> newContactIndices;
284
// First, remove the contacts that aren't from this manager
285
QList<QContact> contacts(request()->contacts());
286
// Try to figure out which of our arguments are new contacts
287
for(int i = 0; i < contacts.count(); i++) {
288
// See if there's a contactId that's not from this manager
289
const QContact c = contacts.at(i);
290
if (c.id().managerUri() == m_engine->managerUri()) {
291
m_existingIdMap.insert(i, existingContactIds.count());
292
existingContactIds.append(c.id());
293
} else if (!c.id().isNull()) {
294
// Hmm, error (wrong manager)
295
m_errorMap.insert(i, QContactManager::DoesNotExistError);
297
newContactIndices.append(i);
301
if (!existingContactIds.isEmpty()) {
302
// Now do the fetch and wait for the result in handleFinishedSubRequest
303
QContactFetchByIdRequest* cfbir = new QContactFetchByIdRequest;
304
cfbir->setIds(existingContactIds);
305
// normally, you'd set the manager, but in this case, we only have a bare engine:
306
QContactManagerEngineV2Wrapper::setEngineOfRequest(cfbir, m_v2wrapper);
307
m_currentSubRequest.reset(cfbir);
308
connect(cfbir, SIGNAL(stateChanged(QContactAbstractRequest::State)),
309
this, SLOT(handleUpdatedSubRequest(QContactAbstractRequest::State)),
310
Qt::QueuedConnection);
311
return cfbir->start();
313
// Special case for the case where every contact is new - we don't need to bother fetching
314
// any existing contacts - just prune them down to the mask and do the save request.
315
QList<QContact> contactsToSave;
316
QSet<QContactDetail::DetailType> mask(request()->typeMask().toSet());
317
for (int i = 0; i < newContactIndices.count(); i++) {
318
QContact contactToSave;
319
partiallyCopyDetails(&contactToSave, contacts[newContactIndices[i]], mask);
320
m_savedToOriginalMap.append(i);
321
contactsToSave.append(contactToSave);
323
QContactSaveRequest* csr = new QContactSaveRequest;
324
csr->setContacts(contactsToSave);
325
// normally, you'd set the manager, but in this case, we only have a bare engine:
326
QContactManagerEngineV2Wrapper::setEngineOfRequest(csr, m_engine);
327
m_currentSubRequest.reset(csr);
328
connect(csr, SIGNAL(stateChanged(QContactAbstractRequest::State)),
329
this, SLOT(handleUpdatedSubRequest(QContactAbstractRequest::State)),
330
Qt::QueuedConnection);
335
/* One of our subrequests has finished. Go to the next step. */
336
void PartialSaveRequestController::handleFinishedSubRequest(QContactAbstractRequest* subReq)
338
if (subReq->type() == QContactAbstractRequest::ContactFetchByIdRequest) {
339
QContactFetchByIdRequest* cfbir = qobject_cast<QContactFetchByIdRequest*>(subReq);
340
QList<QContact> contactsToSave;
341
QMap<int, QContactManager::Error> fetchErrors(cfbir->errorMap());
342
QList<QContact> existingContacts(cfbir->contacts());
343
QList<QContact> contacts(request()->contacts());
344
QSet<QContactDetail::DetailType> mask(request()->typeMask().toSet());
345
for (int i = 0; i < contacts.count(); i++) {
346
// See if this is an existing contact or a new one
347
const int fetchedIdx = m_existingIdMap.value(i, -1);
348
QContact contactToSave;
349
if (fetchedIdx >= 0) {
350
// See if we had an error
351
if (fetchErrors[fetchedIdx] != QContactManager::NoError) {
352
m_errorMap.insert(i, fetchErrors[fetchedIdx]);
356
// Existing contact we should have fetched
357
contactToSave = existingContacts.at(fetchedIdx);
359
QSharedDataPointer<QContactData>& cd = QContactData::contactData(contactToSave);
360
cd->removeOnly(mask);
361
} else if (m_errorMap.contains(i)) {
362
// A bad argument. Leave it out of the contactsToSave list
364
} // else new contact
366
partiallyCopyDetails(&contactToSave, contacts.at(i), mask);
367
m_savedToOriginalMap.append(i);
368
contactsToSave.append(contactToSave);
371
// Now do the fetch and wait for the result
372
QContactSaveRequest* csr = new QContactSaveRequest;
373
csr->setContacts(contactsToSave);
374
// normally, you'd set the manager, but in this case, we only have a bare engine:
375
QContactManagerEngineV2Wrapper::setEngineOfRequest(csr, m_engine);
376
m_currentSubRequest.reset(csr);
377
connect(csr, SIGNAL(stateChanged(QContactAbstractRequest::State)),
378
this, SLOT(handleUpdatedSubRequest(QContactAbstractRequest::State)),
379
Qt::QueuedConnection);
380
csr->start(); // TODO what if this fails?
381
} else if (subReq->type() == QContactAbstractRequest::ContactSaveRequest) {
382
QContactSaveRequest* csr = qobject_cast<QContactSaveRequest*>(subReq);
383
QList<QContact> savedContacts(csr->contacts());
384
QMap<int, QContactManager::Error> saveErrors(csr->errorMap());
385
QList<QContact> contacts(request()->contacts());
386
for (int i = 0; i < savedContacts.count(); i++) {
387
contacts[m_savedToOriginalMap[i]].setId(savedContacts[i].id());
389
// Populate the m_errorMap with the m_errorMap of the attempted save
390
QMap<int, QContactManager::Error>::iterator it(saveErrors.begin());
391
QContactManager::Error error = QContactManager::NoError;
392
while (it != saveErrors.end()) {
394
m_errorMap.insert(m_savedToOriginalMap[it.key()], it.value());
398
// Update the request object
399
QContactManagerEngine::updateContactSaveRequest(
404
QContactAbstractRequest::FinishedState);
411
/* Copy details specified in \a mask from contact \a from to contact \a to */
412
void PartialSaveRequestController::partiallyCopyDetails(QContact* to, const QContact& from,
413
const QSet<QContactDetail::DetailType>& mask) {
414
foreach (QContactDetail::DetailType type, mask) {
415
QList<QContactDetail> details = from.details(type);
416
foreach(QContactDetail detail, details) {
417
to->saveDetail(&detail);
422
#include "moc_qcontactmanagerenginev2wrapper_p.cpp"
424
QT_END_NAMESPACE_CONTACTS