~chris.gagnon/+junk/qtpim-coverage

« back to all changes in this revision

Viewing changes to src/contacts/qcontactmanagerenginev2wrapper_p.cpp

  • Committer: chris.gagnon
  • Date: 2013-12-10 23:09:37 UTC
  • Revision ID: chris.gagnon@canonical.com-20131210230937-2akf1ft1edcttk87
first post

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the QtContacts module of the Qt Toolkit.
 
7
**
 
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.
 
16
**
 
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.
 
24
**
 
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.
 
28
**
 
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.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include "qcontact_p.h"
 
43
#include "qcontactmanagerenginev2wrapper_p.h"
 
44
#include "qcontactidfilter.h"
 
45
#include "qcontactabstractrequest_p.h"
 
46
#include "qcontactfetchbyidrequest.h"
 
47
#include <QDebug>
 
48
 
 
49
QT_BEGIN_NAMESPACE_CONTACTS
 
50
 
 
51
QContactManagerEngineV2Wrapper::QContactManagerEngineV2Wrapper(QContactManagerEngine *wrappee)
 
52
    : m_engine(wrappee)
 
53
{
 
54
    Q_ASSERT(wrappee);
 
55
}
 
56
 
 
57
QContactManagerEngineV2Wrapper::~QContactManagerEngineV2Wrapper()
 
58
{
 
59
    delete m_engine;
 
60
}
 
61
 
 
62
void QContactManagerEngineV2Wrapper::requestDestroyed(QContactAbstractRequest* req)
 
63
{
 
64
    RequestController* controller = m_controllerForRequest.value(req);
 
65
 
 
66
    if (controller) {
 
67
        // If we own it, just delete the controller (and ignore any subrequests' signals from now on)
 
68
        delete controller;
 
69
        m_controllerForRequest.insert(req, 0);
 
70
    } else {
 
71
        m_engine->requestDestroyed(req);
 
72
    }
 
73
}
 
74
 
 
75
bool QContactManagerEngineV2Wrapper::startRequest(QContactAbstractRequest* req)
 
76
{
 
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);
 
88
            return true;
 
89
        } else {
 
90
            return false;
 
91
        }
 
92
    }
 
93
 
 
94
    // Otherwise, pass it on
 
95
    return m_engine->startRequest(req);
 
96
}
 
97
 
 
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)
 
101
{
 
102
    RequestController* controller = qobject_cast<RequestController*>(sender());
 
103
    Q_ASSERT(controller);
 
104
    QContactAbstractRequest* request = controller->request();
 
105
 
 
106
    if (state == QContactAbstractRequest::FinishedState) {
 
107
        delete controller;
 
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
 
110
            // controller
 
111
            m_controllerForRequest.insert(request, 0);
 
112
        }
 
113
    } else {
 
114
        updateRequestState(request, state);
 
115
    }
 
116
 
 
117
}
 
118
 
 
119
/* \reimp */
 
120
bool QContactManagerEngineV2Wrapper::cancelRequest(QContactAbstractRequest* req)
 
121
{
 
122
    if (m_controllerForRequest.contains(req)) {
 
123
        RequestController* controller = m_controllerForRequest.value(req);
 
124
        if (controller) {
 
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)
 
127
            delete controller;
 
128
            m_controllerForRequest.insert(req, 0);
 
129
        }
 
130
        return true;
 
131
    } else {
 
132
        return m_engine->cancelRequest(req);
 
133
    }
 
134
}
 
135
 
 
136
/* \reimp */
 
137
bool QContactManagerEngineV2Wrapper::waitForRequestFinished(QContactAbstractRequest* req, int msecs)
 
138
{
 
139
    if (m_controllerForRequest.contains(req)) {
 
140
        RequestController* controller = m_controllerForRequest.value(req);
 
141
        if (controller) {
 
142
            if (controller->waitForFinished(msecs)) {
 
143
                updateRequestState(req, QContactAbstractRequest::FinishedState);
 
144
                return true;
 
145
            } else {
 
146
                return false;
 
147
            }
 
148
        } else {
 
149
            // A request with a null controller means it has already finished
 
150
            return true;
 
151
        }
 
152
    } else {
 
153
        return m_engine->waitForRequestFinished(req, msecs);
 
154
    }
 
155
 
 
156
}
 
157
 
 
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)
 
161
{
 
162
    request->d_ptr->m_engine = engine;
 
163
}
 
164
 
 
165
 
 
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.
 
172
 */
 
173
 
 
174
/* A slot connected the stateChanged signal of a sub request */
 
175
void RequestController::handleUpdatedSubRequest(QContactAbstractRequest::State state)
 
176
{
 
177
    QObject* caller = sender();
 
178
    QContactAbstractRequest* subRequest = qobject_cast<QContactAbstractRequest*>(caller);
 
179
    if (subRequest) {
 
180
        if (state == QContactAbstractRequest::FinishedState) {
 
181
            // It's possibly already finished if waitForFinished has previously been called
 
182
            if (!isFinished())
 
183
                handleFinishedSubRequest(subRequest);
 
184
        } else {
 
185
            // XXX maybe Canceled should be handled
 
186
        }
 
187
    }
 
188
}
 
189
 
 
190
/* This function handles the rest of the program flow in a synchronous way. */
 
191
bool RequestController::waitForFinished(int msecs)
 
192
{
 
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()) {
 
196
        return false;
 
197
    }
 
198
    while (!isFinished()) {
 
199
        if (!m_currentSubRequest->waitForFinished(msecs))
 
200
            return false;
 
201
        handleFinishedSubRequest(m_currentSubRequest.data());
 
202
    }
 
203
    return true;
 
204
}
 
205
 
 
206
// FetchByIdRequestController stuff
 
207
/* A FetchByIdRequestController is a RequestController for QContactFetchByIdRequests */
 
208
bool FetchByIdRequestController::start()
 
209
{
 
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.
 
212
    Q_ASSERT(m_request);
 
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();
 
226
}
 
227
 
 
228
/* One of our subrequests has finished.  Go to the next step. */
 
229
void FetchByIdRequestController::handleFinishedSubRequest(QContactAbstractRequest* subReq)
 
230
{
 
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.
 
233
    // Take the results:
 
234
    QContactFetchRequest* qcfr = qobject_cast<QContactFetchRequest*>(subReq);
 
235
    QList<QContact> contacts = qcfr->contacts();
 
236
    QContactManager::Error error = qcfr->error();
 
237
 
 
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);
 
243
        }
 
244
    }
 
245
 
 
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());
 
249
 
 
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());
 
260
        } else {
 
261
            results.append(contacts[idMap[id]]);
 
262
        }
 
263
    }
 
264
 
 
265
    // Update the request object
 
266
    QContactManagerEngineV2Wrapper::updateContactFetchByIdRequest(
 
267
            request,
 
268
            results,
 
269
            error,
 
270
            errorMap,
 
271
            QContactAbstractRequest::FinishedState);
 
272
    finish();
 
273
}
 
274
 
 
275
 
 
276
// PartialSaveRequestController stuff
 
277
/* A PartialSaveRequestController is a RequestController for QContactSaveRequests with definition mask */
 
278
bool PartialSaveRequestController::start()
 
279
{
 
280
    // The first step is to fetch the existing contacts.
 
281
    QList<QContactId> existingContactIds;
 
282
    QList<int> newContactIndices;
 
283
 
 
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);
 
296
        } else {
 
297
            newContactIndices.append(i);
 
298
        }
 
299
    }
 
300
 
 
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();
 
312
    } else {
 
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);
 
322
        }
 
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);
 
331
        return csr->start();
 
332
    }
 
333
}
 
334
 
 
335
/* One of our subrequests has finished.  Go to the next step. */
 
336
void PartialSaveRequestController::handleFinishedSubRequest(QContactAbstractRequest* subReq)
 
337
{
 
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]);
 
353
                    continue;
 
354
                }
 
355
 
 
356
                // Existing contact we should have fetched
 
357
                contactToSave = existingContacts.at(fetchedIdx);
 
358
 
 
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
 
363
                continue;
 
364
            } // else new contact
 
365
 
 
366
            partiallyCopyDetails(&contactToSave, contacts.at(i), mask);
 
367
            m_savedToOriginalMap.append(i);
 
368
            contactsToSave.append(contactToSave);
 
369
        }
 
370
 
 
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());
 
388
        }
 
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()) {
 
393
            error = it.value();
 
394
            m_errorMap.insert(m_savedToOriginalMap[it.key()], it.value());
 
395
            it++;
 
396
        }
 
397
 
 
398
        // Update the request object
 
399
        QContactManagerEngine::updateContactSaveRequest(
 
400
                request(),
 
401
                contacts,
 
402
                error,
 
403
                m_errorMap,
 
404
                QContactAbstractRequest::FinishedState);
 
405
        finish();
 
406
    } else {
 
407
        Q_ASSERT(false);
 
408
    }
 
409
}
 
410
 
 
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);
 
418
        }
 
419
    }
 
420
}
 
421
 
 
422
#include "moc_qcontactmanagerenginev2wrapper_p.cpp"
 
423
 
 
424
QT_END_NAMESPACE_CONTACTS